diff --git a/README.adoc b/README.adoc index 71281a24f..70c2df006 100644 --- a/README.adoc +++ b/README.adoc @@ -1,5 +1,14 @@ = Neo4j Cypher Manual +This page covers the following topics: + +* xref:README.adoc#building-locally[] +* xref:README.adoc#raising-prs[] +* xref:README.adoc#documenting-changes[] +* xref:README.adoc#cypher-gql[] + + +[[building-locally]] == Building locally === Prereqs @@ -39,7 +48,7 @@ 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. - +[[raising-prs]] == Raising PRs @@ -68,3 +77,43 @@ There are a few edge cases where we might want to work only on the current branc ** 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. * Updates merged into the `dev` branch for the next release are cherry-picked into the `5.x` branch. + +[[documenting-changes]] +== Documenting changes to Cypher + +New, updated, deprecated, and removed features must be recorded on the xref:modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc[Deprecations, additions, and compatibility] page. + +New and deprecated features should also be marked with a label: + +* If the impacted feature has its own header in the Cypher Manual, use the following: + +.... +[role=label--new-5.x] +== Header +.... + +.... +[role=label--deprecated] +== Header +.... + +* If the impacted feature is documented within a table (such as a return column in a `SHOW` command), use the following: + +`featureX` label:new[Introduced in 5.x] + +`featureY` label:deprecated[] + +Labels can be difficult to apply to updated features. +In these cases, it is often preferable to note the change in a sentence. +For example: "As of Neo4j 5.x, `featureX` supports ..." + +Removed features should be deleted from the Cypher Manual. + +[[cypher-gql]] +== Cypher and GQL + +When documenting a new Cypher feature, its relationship with GQL must be considered: + +* If the feature is part of GQL's mandatory features, it should be recorded on the page xref:modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc[Supported mandatory GQL features]. +* If the feature is part of GQL's optional features, it should be recorded on the page xref:modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc[Supported optional GQL features]. +* if the feature adds functionality for which there exists an analogous, optional GQL feature, it should be recorded on the page xref:modules/ROOT/pages/appendix/gql-conformance/analogous-cypher.adoc[Optional GQL features and analogous Cypher]. +* If the feature adds functionality for which there exists no GQL equivalent, it should be recorded on the page xref:modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc[Additional Cypher features]. diff --git a/antora.yml b/antora.yml index ef51b64c7..3c7aa133b 100644 --- a/antora.yml +++ b/antora.yml @@ -6,6 +6,4 @@ nav: - modules/ROOT/content-nav.adoc asciidoc: attributes: - neo4j-version: '5' - neo4j-version-minor: '5.25' - neo4j-version-exact: '5.25.0' + neo4j-version: '2025.03' diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 2eabb7c4c..d6ea0bd0d 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -94,8 +94,8 @@ ** xref:indexes/syntax.adoc[] * xref:constraints/index.adoc[] +** xref:constraints/managing-constraints.adoc[] ** xref:constraints/syntax.adoc[] -** xref:constraints/examples.adoc[] * xref:planning-and-tuning/index.adoc[] ** xref:planning-and-tuning/execution-plans.adoc[] @@ -116,7 +116,7 @@ ** xref:syntax/parsing.adoc[] ** xref:syntax/naming.adoc[] ** xref:syntax/variables.adoc[] -** xref:syntax/reserved.adoc[] +** xref:syntax/keywords.adoc[] ** xref:syntax/parameters.adoc[] ** xref:syntax/operators.adoc[] ** xref:syntax/comments.adoc[] diff --git a/modules/ROOT/images/call-procedure.svg b/modules/ROOT/images/call-procedure.svg new file mode 100644 index 000000000..e244a4122 --- /dev/null +++ b/modules/ROOT/images/call-procedure.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/call_procedure.svg b/modules/ROOT/images/call_procedure.svg deleted file mode 100644 index 7cb856c93..000000000 --- a/modules/ROOT/images/call_procedure.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSKNOWSDevelopername:'Andy'born:1991Administratorname:'David'born:1994nationality:'Swedish'Developername:'Beatrice'born:1985Administratorname:'Charlotte'born:1990 \ No newline at end of file diff --git a/modules/ROOT/images/call_subquery_graph.svg b/modules/ROOT/images/call_subquery_graph.svg index 932cf79da..e7e2e2629 100644 --- a/modules/ROOT/images/call_subquery_graph.svg +++ b/modules/ROOT/images/call_subquery_graph.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/modules/ROOT/images/case-graph.svg b/modules/ROOT/images/case-graph.svg new file mode 100644 index 000000000..f07b7cab8 --- /dev/null +++ b/modules/ROOT/images/case-graph.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/case_graph.svg b/modules/ROOT/images/case_graph.svg deleted file mode 100644 index 98bfc6ba1..000000000 --- a/modules/ROOT/images/case_graph.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSKNOWSKNOWSMARRIEDPersonname:'Alice'age:38eyes:'brown'Personname:'Charlie'age:53eyes:'green'Personname:'Bob'age:25eyes:'blue'Personname:'Daniel'eyes:'brown'Personname:'Eskil'age:41eyes:'blue' diff --git a/modules/ROOT/images/graph-aggregating-functions.svg b/modules/ROOT/images/graph-aggregating-functions.svg new file mode 100644 index 000000000..241f4e533 --- /dev/null +++ b/modules/ROOT/images/graph-aggregating-functions.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-limit-clause.svg b/modules/ROOT/images/graph-limit-clause.svg new file mode 100644 index 000000000..98315ea57 --- /dev/null +++ b/modules/ROOT/images/graph-limit-clause.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-list-functions.svg b/modules/ROOT/images/graph-list-functions.svg new file mode 100644 index 000000000..25c412407 --- /dev/null +++ b/modules/ROOT/images/graph-list-functions.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-match-clause-backtick.svg b/modules/ROOT/images/graph-match-clause-backtick.svg new file mode 100644 index 000000000..96e0956ff --- /dev/null +++ b/modules/ROOT/images/graph-match-clause-backtick.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-match-clause-variable-length.svg b/modules/ROOT/images/graph-match-clause-variable-length.svg new file mode 100644 index 000000000..c9e67d089 --- /dev/null +++ b/modules/ROOT/images/graph-match-clause-variable-length.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-match-clause.svg b/modules/ROOT/images/graph-match-clause.svg new file mode 100644 index 000000000..605b5c7e6 --- /dev/null +++ b/modules/ROOT/images/graph-match-clause.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-merge-clause.svg b/modules/ROOT/images/graph-merge-clause.svg new file mode 100644 index 000000000..52c21c583 --- /dev/null +++ b/modules/ROOT/images/graph-merge-clause.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-numeric-functions.svg b/modules/ROOT/images/graph-numeric-functions.svg new file mode 100644 index 000000000..2f8442fa0 --- /dev/null +++ b/modules/ROOT/images/graph-numeric-functions.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-optional-match-clause.svg b/modules/ROOT/images/graph-optional-match-clause.svg new file mode 100644 index 000000000..30d557bc7 --- /dev/null +++ b/modules/ROOT/images/graph-optional-match-clause.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-order-by-clause.svg b/modules/ROOT/images/graph-order-by-clause.svg new file mode 100644 index 000000000..3817840e9 --- /dev/null +++ b/modules/ROOT/images/graph-order-by-clause.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-predicate-functions.svg b/modules/ROOT/images/graph-predicate-functions.svg new file mode 100644 index 000000000..12f5b3f19 --- /dev/null +++ b/modules/ROOT/images/graph-predicate-functions.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-remove-clause.svg b/modules/ROOT/images/graph-remove-clause.svg new file mode 100644 index 000000000..9d3eaaebc --- /dev/null +++ b/modules/ROOT/images/graph-remove-clause.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-scalar-functions.svg b/modules/ROOT/images/graph-scalar-functions.svg new file mode 100644 index 000000000..e8e64fffe --- /dev/null +++ b/modules/ROOT/images/graph-scalar-functions.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph-set-clause.svg b/modules/ROOT/images/graph-set-clause.svg new file mode 100644 index 000000000..52fc7173b --- /dev/null +++ b/modules/ROOT/images/graph-set-clause.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph_aggregating_functions.svg b/modules/ROOT/images/graph_aggregating_functions.svg deleted file mode 100644 index a2ec6323e..000000000 --- a/modules/ROOT/images/graph_aggregating_functions.svg +++ /dev/null @@ -1 +0,0 @@ -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/images/graph_limit_clause.svg b/modules/ROOT/images/graph_limit_clause.svg deleted file mode 100644 index 5a21cdd1b..000000000 --- a/modules/ROOT/images/graph_limit_clause.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSKNOWSKNOWSPersonname:'Erika'Personname:'Andy'Personname:'David'Personname:'Charlotte'Personname:'Bernard' \ No newline at end of file diff --git a/modules/ROOT/images/graph_list_functions.svg b/modules/ROOT/images/graph_list_functions.svg deleted file mode 100644 index 82ad32e38..000000000 --- a/modules/ROOT/images/graph_list_functions.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSKNOWSKNOWSMARRIEDAdministratorname:'Charlie'age:53eyes:'Green'Administratorname:'Bob'age:25eyes:'Blue'Developername:'Alice'age:38eyes:'Brown'Administratorname:'Daniel'age:53eyes:'Brown'Designername:'Eskil'age:41eyes:'Blue'likedColors:['Pink', 'Yellow', 'Black'] \ No newline at end of file diff --git a/modules/ROOT/images/graph_match_clause.svg b/modules/ROOT/images/graph_match_clause.svg deleted file mode 100644 index 9426d8918..000000000 --- a/modules/ROOT/images/graph_match_clause.svg +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 3c379d15c..000000000 --- a/modules/ROOT/images/graph_match_clause_backtick.svg +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 8c79b6f6d..000000000 --- a/modules/ROOT/images/graph_match_clause_variable_length.svg +++ /dev/null @@ -1 +0,0 @@ -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/images/graph_merge_clause.svg b/modules/ROOT/images/graph_merge_clause.svg deleted file mode 100644 index ac3e96690..000000000 --- a/modules/ROOT/images/graph_merge_clause.svg +++ /dev/null @@ -1 +0,0 @@ -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/images/graph_numeric_functions.svg b/modules/ROOT/images/graph_numeric_functions.svg deleted file mode 100644 index 82ad32e38..000000000 --- a/modules/ROOT/images/graph_numeric_functions.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSKNOWSKNOWSMARRIEDAdministratorname:'Charlie'age:53eyes:'Green'Administratorname:'Bob'age:25eyes:'Blue'Developername:'Alice'age:38eyes:'Brown'Administratorname:'Daniel'age:53eyes:'Brown'Designername:'Eskil'age:41eyes:'Blue'likedColors:['Pink', 'Yellow', 'Black'] \ No newline at end of file diff --git a/modules/ROOT/images/graph_optional_match_clause.svg b/modules/ROOT/images/graph_optional_match_clause.svg deleted file mode 100644 index d5373d409..000000000 --- a/modules/ROOT/images/graph_optional_match_clause.svg +++ /dev/null @@ -1 +0,0 @@ -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/images/graph_order_by_clause.svg b/modules/ROOT/images/graph_order_by_clause.svg deleted file mode 100644 index a42046fcc..000000000 --- a/modules/ROOT/images/graph_order_by_clause.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSPersonname:'Charlotte'age:32length:185Personname:'Bernard'age:36Personname:'Andy'age:34length:170 \ No newline at end of file diff --git a/modules/ROOT/images/graph_predicate_functions.svg b/modules/ROOT/images/graph_predicate_functions.svg deleted file mode 100644 index 9b8a6a64d..000000000 --- a/modules/ROOT/images/graph_predicate_functions.svg +++ /dev/null @@ -1 +0,0 @@ -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/graph_remove_clause.svg b/modules/ROOT/images/graph_remove_clause.svg deleted file mode 100644 index 534698e78..000000000 --- a/modules/ROOT/images/graph_remove_clause.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSSwedishname:'Andy'age:36propTestValue2:42Swedishname:'Timothy'age:25propTestValue2:42SwedishGermanname:'Peter'age:34 \ No newline at end of file diff --git a/modules/ROOT/images/graph_scalar_functions.svg b/modules/ROOT/images/graph_scalar_functions.svg deleted file mode 100644 index 82ad32e38..000000000 --- a/modules/ROOT/images/graph_scalar_functions.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSKNOWSKNOWSMARRIEDAdministratorname:'Charlie'age:53eyes:'Green'Administratorname:'Bob'age:25eyes:'Blue'Developername:'Alice'age:38eyes:'Brown'Administratorname:'Daniel'age:53eyes:'Brown'Designername:'Eskil'age:41eyes:'Blue'likedColors:['Pink', 'Yellow', 'Black'] \ No newline at end of file diff --git a/modules/ROOT/images/graph_set_clause.svg b/modules/ROOT/images/graph_set_clause.svg deleted file mode 100644 index 61126d48d..000000000 --- a/modules/ROOT/images/graph_set_clause.svg +++ /dev/null @@ -1 +0,0 @@ -KNOWSKNOWSKNOWSname:'Stefan'Swedishname:'Andy'age:36hungry:truename:'George'name:'Peter'age:34 \ No newline at end of file diff --git a/modules/ROOT/images/graph_skip_clause.svg b/modules/ROOT/images/graph_skip_clause.svg index 5a21cdd1b..036c103b9 100644 --- a/modules/ROOT/images/graph_skip_clause.svg +++ b/modules/ROOT/images/graph_skip_clause.svg @@ -1 +1,32 @@ -KNOWSKNOWSKNOWSKNOWSPersonname:'Erika'Personname:'Andy'Personname:'David'Personname:'Charlotte'Personname:'Bernard' \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph_union_clause.svg b/modules/ROOT/images/graph_union_clause.svg index 17a159aec..56a3619bd 100644 --- a/modules/ROOT/images/graph_union_clause.svg +++ b/modules/ROOT/images/graph_union_clause.svg @@ -1 +1,18 @@ -ACTED_INACTED_INActorname:'Johnny Depp'Actorname:'Sarah Jessica Parker'Movietitle:'Ed woodActorDirectorname:'Ed Wood' \ No newline at end of file + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/graph_where_clause.svg b/modules/ROOT/images/graph_where_clause.svg index b307c8355..6a1b83c80 100644 --- a/modules/ROOT/images/graph_where_clause.svg +++ b/modules/ROOT/images/graph_where_clause.svg @@ -1 +1,15 @@ -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/images/introduction-example1.svg b/modules/ROOT/images/introduction-example1.svg new file mode 100644 index 000000000..9ffe1ae4e --- /dev/null +++ b/modules/ROOT/images/introduction-example1.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/introduction_example1.svg b/modules/ROOT/images/introduction_example1.svg deleted file mode 100644 index 82738cb33..000000000 --- a/modules/ROOT/images/introduction_example1.svg +++ /dev/null @@ -1 +0,0 @@ -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 index bb0636913..fb4c99ee4 100644 --- a/modules/ROOT/images/introduction_example2.svg +++ b/modules/ROOT/images/introduction_example2.svg @@ -1 +1,20 @@ -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/patterns_equijoins.svg b/modules/ROOT/images/patterns_equijoins.svg index 0a2c996be..5729fbb6a 100644 --- a/modules/ROOT/images/patterns_equijoins.svg +++ b/modules/ROOT/images/patterns_equijoins.svg @@ -1 +1,118 @@ -CALLS_ATCALLS_ATNEXTCALLS_ATCALLS_ATCALLS_ATNEXTNEXTCALLS_ATname:Coventrydeparts:15:54name:London Eustonname:Birmingham Int'ldeparts:08:40departs:14:45arrives:09:34departs:11:33departs:12:03 \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_equijoins_motif.svg b/modules/ROOT/images/patterns_equijoins_motif.svg index b67a84c16..eeb84153c 100644 --- a/modules/ROOT/images/patterns_equijoins_motif.svg +++ b/modules/ROOT/images/patterns_equijoins_motif.svg @@ -1 +1,25 @@ -CALLS_ATNEXTCALLS_ATCALLS_ATNEXTCALLS_ATname:Coventrynname:London Eustonname:London Euston \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_equijoins_motif2.svg b/modules/ROOT/images/patterns_equijoins_motif2.svg index 145afe3f7..5c2d34a68 100644 --- a/modules/ROOT/images/patterns_equijoins_motif2.svg +++ b/modules/ROOT/images/patterns_equijoins_motif2.svg @@ -1 +1,25 @@ -CALLS_ATNEXTCALLS_ATCALLS_ATNEXTCALLS_ATname:Coventrynname:London Eustonn \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_equijoins_motif3.svg b/modules/ROOT/images/patterns_equijoins_motif3.svg index 145afe3f7..9bceaa66e 100644 --- a/modules/ROOT/images/patterns_equijoins_motif3.svg +++ b/modules/ROOT/images/patterns_equijoins_motif3.svg @@ -1 +1,25 @@ -CALLS_ATNEXTCALLS_ATCALLS_ATNEXTCALLS_ATname:Coventrynname:London Eustonn \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_equijoins_solution.svg b/modules/ROOT/images/patterns_equijoins_solution.svg index b750a9c6e..ccf057856 100644 --- a/modules/ROOT/images/patterns_equijoins_solution.svg +++ b/modules/ROOT/images/patterns_equijoins_solution.svg @@ -1,9 +1,196 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_equijoins_solution2.svg b/modules/ROOT/images/patterns_equijoins_solution2.svg index a199ab2f0..66d37acc8 100644 --- a/modules/ROOT/images/patterns_equijoins_solution2.svg +++ b/modules/ROOT/images/patterns_equijoins_solution2.svg @@ -1 +1,100 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_qpp_motif1.svg b/modules/ROOT/images/patterns_qpp_motif1.svg index 513f6d125..94e9aa930 100644 --- a/modules/ROOT/images/patterns_qpp_motif1.svg +++ b/modules/ROOT/images/patterns_qpp_motif1.svg @@ -1 +1,18 @@ -CALLS_ ATCALLS_ATNEXTNEXTNEXTCallingPointCallingPointCallingPointCallingPoint \ No newline at end of file + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_second_shortest_paths.svg b/modules/ROOT/images/patterns_second_shortest_paths.svg index 80b0db29f..8345677fc 100644 --- a/modules/ROOT/images/patterns_second_shortest_paths.svg +++ b/modules/ROOT/images/patterns_second_shortest_paths.svg @@ -1 +1,65 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_shortest_graph.svg b/modules/ROOT/images/patterns_shortest_graph.svg index 0f946e38e..a02e83e2b 100644 --- a/modules/ROOT/images/patterns_shortest_graph.svg +++ b/modules/ROOT/images/patterns_shortest_graph.svg @@ -1 +1,72 @@ -6.030.655.763.715.646.1631.147.2511.2914.7512.64.16HartleburyDroitwichSpaWorcestershireParkwayPershoreWorcesterForegateStreetWorcesterShrub HillBromsgroveCheltenhamSpaAshchurch \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_shortest_graph_pattern1.svg b/modules/ROOT/images/patterns_shortest_graph_pattern1.svg index b1f8982bd..71c7885e7 100644 --- a/modules/ROOT/images/patterns_shortest_graph_pattern1.svg +++ b/modules/ROOT/images/patterns_shortest_graph_pattern1.svg @@ -1 +1,17 @@ -DroitwichSpaWorcestershireParkwayWorcesterShrub HillBromsgroveCheltenhamSpa \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_shortest_graph_pattern2.svg b/modules/ROOT/images/patterns_shortest_graph_pattern2.svg index f19ab73aa..c16306b5e 100644 --- a/modules/ROOT/images/patterns_shortest_graph_pattern2.svg +++ b/modules/ROOT/images/patterns_shortest_graph_pattern2.svg @@ -1 +1,17 @@ -DroitwichSpaWorcestershireParkwayWorcesterShrub HillBromsgroveCheltenhamSpa \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_shortest_tie.svg b/modules/ROOT/images/patterns_shortest_tie.svg index 99d2f9240..d0113abad 100644 --- a/modules/ROOT/images/patterns_shortest_tie.svg +++ b/modules/ROOT/images/patterns_shortest_tie.svg @@ -1 +1,23 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/patterns_shortestpath.svg b/modules/ROOT/images/patterns_shortestpath.svg index 2bb3d06ed..d5838b616 100644 --- a/modules/ROOT/images/patterns_shortestpath.svg +++ b/modules/ROOT/images/patterns_shortestpath.svg @@ -1 +1,24 @@ -Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)CALLS_ATNEXTCALLS_ATCALLS_ATNEXTCALLS_AT Clapham High Street Elephant Cas… "11:41:0… "11:37:0… Denmark Hill "11:47:0… "11:54:0… \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/predicate_function_example.svg b/modules/ROOT/images/predicate_function_example.svg index 8b6919eb0..7c0fe2032 100644 --- a/modules/ROOT/images/predicate_function_example.svg +++ b/modules/ROOT/images/predicate_function_example.svg @@ -1 +1,12 @@ -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/images/values_and_types_lists_graph.svg b/modules/ROOT/images/values_and_types_lists_graph.svg index 8c71adee4..3fde574ac 100644 --- a/modules/ROOT/images/values_and_types_lists_graph.svg +++ b/modules/ROOT/images/values_and_types_lists_graph.svg @@ -1 +1,47 @@ -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/pages/administration/index.adoc b/modules/ROOT/pages/administration/index.adoc index cac8d8e33..fa9d6119b 100644 --- a/modules/ROOT/pages/administration/index.adoc +++ b/modules/ROOT/pages/administration/index.adoc @@ -1,6 +1,6 @@ = Administration -The pages previously in this chapter have been moved to the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/[Operations Manual]. +The pages previously in this chapter have been moved to the link:{neo4j-docs-base-uri}/operations-manual/current/[Operations Manual]. More specific information about the content relocation is listed in the table: @@ -9,13 +9,13 @@ More specific information about the content relocation is listed in the table: | Content | New location in Operations Manual -| Database management | link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/[Database administration] +| Database management | link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[Database administration] -| Alias management | link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/aliases/manage-aliases-standard-databases/[Managing aliases] +| Alias management | link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/aliases/manage-aliases-standard-databases/[Managing aliases] -| Server management | link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/[Managing servers in a cluster] +| Server management | link:{neo4j-docs-base-uri}/operations-manual/current/clustering/[Managing servers in a cluster] -| Access control | link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/[Authentication and authorization] +| Access control | link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/[Authentication and authorization] |=== diff --git a/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc b/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc index 2a33e5d4c..fd877826e 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc @@ -1,4 +1,5 @@ :description: Information about Cypher features not included in GQL. +:test-skip: true = Additional Cypher features While the link:https://www.iso.org/standard/76120.html[GQL Standard] incorporates a lot of capabilities in Cypher, Cypher contains additional features that are not part of GQL and no GQL alternatives currently exist for them. @@ -78,6 +79,93 @@ Either the pattern already exists, or it needs to be created. | Syntactic construct for creating a `LIST` based on matchings of a pattern. |=== +[[dynamic-queries]] +== Dynamic queries + +Node labels, relationship types, properties, and CSV columns can be referenced dynamically using Cypher. +This allows for more flexible queries and mitigates the risk of Cypher injection. +(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]). + +[options="header", cols="2a,5a"] +|=== +| Cypher feature +| Description + +a| +[source, cypher, role="noheader"] +---- +MATCH (n:$($label)), + ()-[r:$($type)]->() +---- + +| xref:clauses/match.adoc#dynamic-match[`MATCH` nodes and relationships using dynamic node labels and relationship types] + +a| +[source, cypher, role="noheader"] +---- +CREATE (n:$($label)), + ()-[r:$($type)]->() +---- + +| xref:clauses/create.adoc#dynamic-create[`CREATE` nodes and relationships using dynamic node labels and relationship types] + +a| +[source, cypher, role="noheader"] +---- +MERGE (n:$($label)), + ()-[r:$($type)]->() +---- + +| xref:clauses/merge.adoc#dynamic-merge[`MERGE` nodes and relationships using dynamic node labels and relationship types] + +a| +[source, cypher, role="noheader"] +---- +LOAD CSV WITH HEADERS FROM 'file:///artists-with-headers.csv' AS line +CREATE (n:$(line.label) {name: line.Name}) +---- + +| xref:clauses/load-csv.adoc#dynamic-columns[Import CSV files using dynamic columns] + + +a| +[source, cypher, role="noheader"] +---- +MATCH (n) +SET n[$key] = value +---- + +| xref:clauses/set.adoc#dynamic-set-property[Dynamically `SET` or update a property] + +a| +[source, cypher, role="noheader"] +---- +MATCH (n:Label) +SET n:$(n.property) +---- + +| xref:clauses/set.adoc#dynamic-set-node-label[Dynamically `SET` a node label] + +a| +[source, cypher, role="noheader"] +---- +MATCH (n {name: 'Peter'}) +REMOVE n:$($label) +---- + +| xref:clauses/remove.adoc#dynamic-remove-property[Dynamically `REMOVE` a property] + +a| +[source, cypher, role="noheader"] +---- +MATCH (n {name: 'Peter'}) +REMOVE n:$($label) +---- + +| xref:clauses/remove.adoc#dynamic-remove-node-label[Dynamically `REMOVE` a node label] + +|=== + [[functions]] == Functions @@ -118,7 +206,10 @@ Either the pattern already exists, or it needs to be created. | Description | xref:functions/graph.adoc#functions-graph-by-elementid[`graph.byElementId()`] -| Returns the graph reference with the given element id. It is only supported in the `USE` clause, on composite databases. +| Returns the graph reference with the given element id. +It is only supported in the xref:clauses/use.adoc[`USE`] clause. +As of Neo4j 5.26, it is supported on both link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[standard and composite databases]. +On earlier versions, it is only supported on composite databases. | xref:functions/graph.adoc#functions-graph-byname[`graph.byName()`] | Returns the graph reference of the given name. It is only supported in the `USE` clause, on composite databases. @@ -301,7 +392,9 @@ Either the pattern already exists, or it needs to be created. | Returns a 2D or 3D point object, given two or respectively three coordinate values in the Cartesian coordinate system or WGS 84 geographic coordinate system. | xref:functions/spatial.adoc#functions-point-distance[`point.distance()`] -| Returns a `FLOAT` representing the geodesic distance between any two points in the same CRS. +| Returns a `FLOAT` representing the distance between any two points in the same CRS. +If the points are in the WGS 84 CRS, the function returns the geodesic distance (i.e., the shortest path along the curved surface of the Earth). +If the points are in a Cartesian CRS, the function returns the Euclidean distance (i.e., the shortest straight-line distance in a flat, planar space). | xref:functions/spatial.adoc#functions-point-withinBBox[`point.withinBBox()`] | Returns true if the provided point is within the bounding box defined by the two provided points. @@ -507,29 +600,17 @@ GQL supports `GRAPH TYPES` as a way of constraining a graph schema, but does not | Cypher feature | Description -| xref:constraints/examples.adoc#constraints-examples-node-uniqueness[Node property uniqueness constraints] -| Ensures that certain nodes have a set of specified properties whose combined value is unique when all properties exist on the node - -| xref:constraints/examples.adoc#constraints-examples-relationship-uniqueness[Relationship property uniqueness constraints] -| Ensures that certain relationships have a set of specified properties whose combined value is unique when all properties exist on the relationship. - -| xref:constraints/examples.adoc#constraints-examples-node-property-existence[Node property existence constraints] -| Ensures that certain nodes have a specified property. - -| xref:constraints/examples.adoc#constraints-examples-relationship-property-existence[Relationship property existence constraints] -| Ensures that certain relationships have a specified property. - -| xref:constraints/examples.adoc#constraints-examples-node-property-type[Node property type constraints] -| Ensures that certain nodes have a property of the required property type when the property exists on the node. +| xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[Property uniqueness constraints] +| Ensures that the combined property values are unique for all nodes with a specific label or all relationships with a specific type. -| xref:constraints/examples.adoc#constraints-examples-relationship-property-type[Relationship property type constraints] -| Ensures that certain relationships have a property of the required property type when the property exists on the relationship. +| xref:constraints/managing-constraints.adoc#create-property-existence-constraints[Property existence constraints] +| Ensures that a property exists either for all nodes with a specific label or for all relationships with a specific type. -| xref:constraints/examples.adoc#constraints-examples-node-key[Node key constraints] -| Ensures that certain nodes have a set of specified properties whose combined value is unique and all properties in the set are present. +| xref:constraints/managing-constraints.adoc#create-property-type-constraints[Property type constraints] +| Ensures that a property has the required property type for all nodes with a specific label or for all relationships with a specific type. -| xref:constraints/examples.adoc#constraints-examples-relationship-key[Relationship key constraints] -| Ensures that certain relationships have a set of defined properties whose combined value is unique. It also ensures that all properties in the set are present. +| xref:constraints/managing-constraints.adoc#create-key-constraints[Key constraints] +| Ensures that all properties exist and that the combined property values are unique for all nodes with a specific label or all relationships with a specific type. |=== @@ -577,23 +658,23 @@ For more information, see xref:planning-and-tuning/query-tuning.adoc[Query optio == Administration [NOTE] -The documentation for Cypher's administration commands is located in Neo4j's link:{neo4j-docs-base-uri}/operations-manual/{page-version}/[Operation Manual]. +The documentation for Cypher's administration commands is located in Neo4j's link:{neo4j-docs-base-uri}/operations-manual/current/[Operation Manual]. [options="header", cols="2a,5a"] |=== | Cypher feature | Description -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/[Database management] +| link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[Database management] | Commands to `CREATE`, `SHOW`, `ALTER`, and `DROP` standard and composite databases. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/aliases/manage-aliases-standard-databases/[Alias management] +| link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/aliases/manage-aliases-standard-databases/[Alias management] | Commands to `CREATE`, `SHOW`, `ALTER`, and `DROP` database aliases. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/[Server management] +| link:{neo4j-docs-base-uri}/operations-manual/current/clustering/[Server management] | Commands to administer servers in a cluster and the databases allocated to them. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/[Authentication and authorization] +| link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/[Authentication and authorization] | Commands to manage users, roles, and privileges. |=== \ No newline at end of file diff --git a/modules/ROOT/pages/appendix/gql-conformance/index.adoc b/modules/ROOT/pages/appendix/gql-conformance/index.adoc index 2aafc9772..438f89042 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/index.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/index.adoc @@ -1,8 +1,8 @@ :description: Overview of Cypher's conformance to GQL. = GQL conformance -*Last updated*: 13 September 2024 + -*Neo4j version*: 5.24 +*Last updated*: 24 October 2024 + +*Neo4j version*: 5.25 GQL is the new link:https://www.iso.org/home.html[ISO] International Standard query language for graph databases. diff --git a/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc b/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc index 6a8299b0e..4551dd8f6 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc @@ -155,7 +155,7 @@ This is currently not available in Cypher. | | | GQL defines the `SESSION_USER` value expression, which enables accessing a user’s username within a query. -In Cypher, current user details can be seen using the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/manage-users/#access-control-current-users[`SHOW CURRENT USER` command]. +In Cypher, current user details can be seen using the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/manage-users/#access-control-current-users[`SHOW CURRENT USER` command]. | 20.7 | diff --git a/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc b/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc index b71364b84..77defa1ec 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc @@ -93,6 +93,11 @@ These codes order the features in the table below. | xref:patterns/reference.adoc#graph-patterns-rules-variable-references[Graph patterns -> Rules] | +| GB01 +| Long identifiers +| xref:syntax/naming.adoc#identifier-length-limit[Naming rules and recommendations -> Identifier length limit] +| + | GF01 | Enhanced numeric functions | xref:functions/mathematical-numeric.adoc#functions-abs[`abs()`], xref:functions/mathematical-numeric.adoc#functions-floor[`floor()`], xref:functions/mathematical-logarithmic.adoc#functions-sqrt[`sqrt()`]. diff --git a/modules/ROOT/pages/appendix/gql-conformance/unsupported-mandatory.adoc b/modules/ROOT/pages/appendix/gql-conformance/unsupported-mandatory.adoc index 98c143dcb..c81f453a5 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/unsupported-mandatory.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/unsupported-mandatory.adoc @@ -28,7 +28,7 @@ Neo4j offers session management through the link:{neo4j-docs-base-uri}/create-ap | GQL defines the following transaction commands: `START TRANSACTION`, `COMMIT`, and `ROLLBACK`. Neo4j offers transaction management through the link:{neo4j-docs-base-uri}/create-applications[driver] transaction API. -Cypher Shell also offers specific link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/cypher-shell/#cypher-shell-commands[commands] to manage transactions. +Cypher Shell also offers specific link:{neo4j-docs-base-uri}/operations-manual/current/tools/cypher-shell/#cypher-shell-commands[commands] to manage transactions. | 11.1 | Graph expressions @@ -41,5 +41,5 @@ Cypher Shell also offers specific link:{neo4j-docs-base-uri}/operations-manual/{ | 21.3 | , , and | GQL specifies a list of link:https://standards.iso.org/iso-iec/39075/ed-1/en/ISO_IEC_39075(en).bnf.txt[reserved words] that cannot be used for unquoted variable names, labels, and property names. -Cypher also specifies a list of xref:syntax/reserved.adoc[reserved keywords], but it differs from GQL's. +Cypher also specifies a list of xref:syntax/keywords.adoc[reserved keywords], but it differs from GQL's. |=== \ No newline at end of file diff --git a/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc b/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc index 910b35f78..5d5816465 100644 --- a/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc +++ b/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc @@ -15,8 +15,8 @@ Let's explain how to use these features with a more advanced query tuning exampl [NOTE] ==== -If you are upgrading an existing store to {neo4j-version-exact}, it may be necessary to drop and re-create existing indexes. -For information on native index support and upgrade considerations regarding indexes, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/performance/index-configuration[Operations Manual -> Performance -> Index configuration]. +If you are upgrading an existing store, it may be necessary to drop and re-create existing indexes. +For information on native index support and upgrade considerations regarding indexes, see link:{neo4j-docs-base-uri}/operations-manual/current/performance/index-configuration[Operations Manual -> Performance -> Index configuration]. ==== @@ -540,8 +540,8 @@ It assumes that your current work directory is the __ directory of t [NOTE] ==== -* For the default directory of other installations see, link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/file-locations[Operations Manual -> File locations]. -* The import location can be configured with link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings#config_server.directories.import[Operations Manual -> `server.directories.import`]. +* For the default directory of other installations see, link:{neo4j-docs-base-uri}/operations-manual/current/configuration/file-locations[Operations Manual -> File locations]. +* The import location can be configured with link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings#config_server.directories.import[Operations Manual -> `server.directories.import`]. ==== == Importing the data @@ -669,7 +669,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -711,7 +711,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -776,7 +776,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -823,7 +823,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -878,7 +878,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -936,10 +936,10 @@ Predicates that will not work: [NOTE] ==== -If there is an existence constraint on the property, no predicate is required to trigger the optimization. +If there is a xref:constraints/managing-constraints.adoc#create-property-existence-constraints[property 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_. +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`. ==== diff --git a/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc b/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc index 6b30f55a0..faa961fe0 100644 --- a/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc +++ b/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc @@ -525,8 +525,8 @@ It assumes that your current work directory is the __ directory of t [NOTE] ==== -* For the default directory of other installations see, link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/file-locations[Operations Manual -> File locations]. -* The import location can be configured with link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings#config_server.directories.import[Operations Manual -> `server.directories.import`]. +* For the default directory of other installations see, link:{neo4j-docs-base-uri}/operations-manual/current/configuration/file-locations[Operations Manual -> File locations]. +* The import location can be configured with link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings#config_server.directories.import[Operations Manual -> `server.directories.import`]. ==== == Importing the data @@ -623,7 +623,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -680,7 +680,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -747,7 +747,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 diff --git a/modules/ROOT/pages/appendix/tutorials/shortestpath-planning.adoc b/modules/ROOT/pages/appendix/tutorials/shortestpath-planning.adoc index 64b0a5233..40df8bc60 100644 --- a/modules/ROOT/pages/appendix/tutorials/shortestpath-planning.adoc +++ b/modules/ROOT/pages/appendix/tutorials/shortestpath-planning.adoc @@ -20,7 +20,7 @@ When the exhaustive search is planned, it is still only executed when the fast a The fast algorithm is always executed first, since it is possible that it can find a valid path even though that could not be guaranteed at planning time. Please note that falling back to the exhaustive search may prove to be a very time consuming strategy in some cases; such as when there is no shortest path between two nodes. -Therefore, in these cases, it is recommended to set `cypher.forbid_exhaustive_shortestpath` to `true`, as explained in link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings#config_dbms.cypher.forbid_exhaustive_shortestpath[Operations Manual -> Configuration settings]. +Therefore, in these cases, it is recommended to set `cypher.forbid_exhaustive_shortestpath` to `true`, as explained in link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings#config_dbms.cypher.forbid_exhaustive_shortestpath[Operations Manual -> Configuration settings]. == Shortest path -- fast algorithm @@ -81,7 +81,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -158,7 +158,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 1024 @@ -261,7 +261,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 diff --git a/modules/ROOT/pages/clauses/call.adoc b/modules/ROOT/pages/clauses/call.adoc index d600d0bd4..03763efd0 100644 --- a/modules/ROOT/pages/clauses/call.adoc +++ b/modules/ROOT/pages/clauses/call.adoc @@ -12,16 +12,16 @@ For information about how to list procedures, see xref:clauses/listing-procedure [NOTE] Neo4j comes with a number of built-in procedures. -For a list of these, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures[Operations Manual -> Procedures]. +For a list of these, see link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures[Operations Manual -> Procedures]. Users can also develop custom procedures and deploy to the database. -See link:{neo4j-docs-base-uri}/java-reference/{page-version}/extending-neo4j/procedures#extending-neo4j-procedures[Java Reference -> User-defined procedures] for details. +See link:{neo4j-docs-base-uri}/java-reference/current/extending-neo4j/procedures#extending-neo4j-procedures[Java Reference -> User-defined procedures] for details. [[example-graph]] == Example graph The following graph is used for the examples below: -image::call_procedure.svg[width="400",role="middle"] +image::call-procedure.svg[Example graph connecting people in the roles of developer and administrator,role=popup,width=400] To recreate it, run the following query against an empty Neo4j database: @@ -42,7 +42,7 @@ CREATE (andy:Developer {name: 'Andy', born: 1991}), .`CALL` a procedure without arguments ==== -This example calls the built-in procedure link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_labels[`db.labels()`], which lists all labels used in the database. +This example calls the built-in procedure link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_labels[`db.labels()`], which lists all labels used in the database. .Query [source, cypher] @@ -72,7 +72,7 @@ Omission of parentheses is available only in a so-called standalone procedure ca ==== -This example calls the procedure link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_checkconfigvalue[`dbms.checkConfigValue()`], which checks the validity of a configuration setting value, using literal arguments. +This example calls the procedure link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_checkconfigvalue[`dbms.checkConfigValue()`], which checks the validity of a configuration setting value, using literal arguments. .Query [source, cypher] @@ -86,7 +86,7 @@ CALL dbms.checkConfigValue('server.bolt.enabled', 'true') | "valid" | "message" | true | "requires restart" -1+d|Rows: 2 +2+d|Rows: 1 |=== ==== @@ -118,7 +118,7 @@ CALL dbms.checkConfigValue($setting, $value) | "valid" | "message" | true | "requires restart" -1+d|Rows: 2 +2+d|Rows: 1 |=== [NOTE] @@ -153,7 +153,7 @@ CALL dbms.checkConfigValue($setting, 'true') | "valid" | "message" | true | "requires restart" -1+d|Rows: 2 +2+d|Rows: 1 |=== ==== @@ -189,6 +189,8 @@ CALL db.labels() YIELD * If the procedure has deprecated return columns, those columns are also returned. Note that `YIELD *` is only valid in standalone procedure calls. +Variables must be explicitly named in a `YIELD` clause if other clauses than a single procedure `CALL` are present. +This restriction simplifies query logic and protects against output variables from the procedure accidentally clashing with other query variables. For example, the following is not valid: .Not allowed @@ -204,7 +206,7 @@ RETURN count(*) AS results ==== `YIELD` can be used to filter for specific results. -This requires knowing the names of the arguments within a procedure's signature, which can either be found in the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/[Operations Manual -> Procedures] or returned by a `SHOW PROCEDURES` query. +This requires knowing the names of the arguments within a procedure's signature, which can either be found in the link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/[Operations Manual -> Procedures] or in the `signature` column returned by a `SHOW PROCEDURES` command (see example below). .Find the argument names of `db.propertyKeys` [source, cypher] @@ -269,7 +271,7 @@ Similar to xref:clauses/optional-match.adoc[`OPTIONAL MATCH`] any empty rows pro .Difference between using `CALL` and `OPTIONAL CALL` ==== -This query uses the link:{neo4j-docs-base-uri}/apoc/{page-version}/overview/apoc.neighbors/apoc.neighbors.tohop[`apoc.neighbors.tohop()`] procedure (part of Neo4j's link:{neo4j-docs-base-uri}/apoc/{page-version}/[APOC Core library]), which returns all nodes connected by the given relationship type within the specified distance (1 hop, in this case) and direction. +This query uses the link:{neo4j-docs-base-uri}/apoc/current/overview/apoc.neighbors/apoc.neighbors.tohop[`apoc.neighbors.tohop()`] procedure (part of Neo4j's link:{neo4j-docs-base-uri}/apoc/current/[APOC Core library]), which returns all nodes connected by the given relationship type within the specified distance (1 hop, in this case) and direction. .Regular procedure `CALL` [source, cypher] diff --git a/modules/ROOT/pages/clauses/create.adoc b/modules/ROOT/pages/clauses/create.adoc index 9749ac375..e5e1f7193 100644 --- a/modules/ROOT/pages/clauses/create.adoc +++ b/modules/ROOT/pages/clauses/create.adoc @@ -205,12 +205,66 @@ Nodes created: 2 + Properties set: 4 |=== +[role=label--new-5.26] +[[dynamic-create]] +== CREATE using dynamic node labels and relationship types + +Node labels and relationship types can be referenced dynamically in expressions, parameters, and variables when creating nodes and relationships. +This allows for more flexible queries and mitigates the risk of Cypher injection. +(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]). + +.Syntax for creating nodes and relationships dynamically +[source, syntax] +---- +CREATE (n:$()) +CREATE ()-[r:$()]->() +---- + +The expression must evaluate to a `STRING NOT NULL | LIST NOT NULL` value. +Using a `LIST` with more than one item when creating a relationship using dynamic relationship types will fail. +This is because a relationship can only have exactly one type. + +.Parameters +[source, parameters] +---- +{ + "nodeLabels": ["Person", "Director"], + "relType": "DIRECTED", + "movies": ["Ladybird", "Little Women", "Barbie"] +} +---- + +.Create nodes and relationships using dynamic node labels and relationship types +// tag::clauses_create_dynamic_create[] +[source, cypher] +---- +CREATE (greta:$($nodeLabels) {name: 'Greta Gerwig'}) +WITH greta +UNWIND $movies AS movieTitle +CREATE (greta)-[rel:$($relType)]->(m:Movie {title: movieTitle}) +RETURN greta.name AS name, labels(greta) AS labels, type(rel) AS relType, collect(m.title) AS movies +---- +// end::clauses_create_dynamic_create[] + +.Result +[role="queryresult",options="footer",cols="4* Space reuse]. +For information about how to clear and reuse the space occupied by deleted objects, see link:{neo4j-docs-base-uri}/operations-manual/current/performance/space-reuse/[Operations Manual -> Space reuse]. == Example graph @@ -120,16 +120,16 @@ Deleted 1 node, deleted 1 relationship [NOTE] ==== 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]. +For more information, see link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/access-control#detach-delete-restricted-user[Operations Manual -> Fine-grained access control]. ==== [[delete-all-nodes-and-relationships]] == Delete all nodes and relationships -It is possible to delete all nodes and relationships in a graph. +It is possible to delete all nodes and relationships in a graph. -.Query +.Delete all nodes and relationships [source, cypher, indent=0] ---- MATCH (n) @@ -142,8 +142,25 @@ DETACH DELETE n Deleted 3 nodes, deleted 1 relationship ---- -[TIP] -==== -`DETACH DELETE` is not suitable for deleting large amounts of data, but is useful when experimenting with small example datasets. -To delete large amounts of data, instead use xref::subqueries/subqueries-in-transactions.adoc#delete-with-call-in-transactions[CALL subqueries in transactions]. -==== \ No newline at end of file +`DETACH DELETE` is useful when experimenting with small example datasets, but it is not suitable for deleting large amounts of data, nor does it delete xref:indexes/search-performance-indexes/overview.adoc[indexes] and xref:constraints/index.adoc[constraints]. + +To delete large amounts of data without deleting indexes and constraints, use xref::subqueries/subqueries-in-transactions.adoc#delete-with-call-in-transactions[CALL subqueries in transactions] instead. + +.Delete all nodes and relationships using `CALL` subqueries +[source, cypher] +---- +MATCH (n) +CALL (n) { + DETACH DELETE n +} IN TRANSACTIONS +---- + +To remove all data, including indexes and constraints, recreate the database using the following command: `CREATE OR REPLACE DATABASE name`. + +.Delete a database and recreate it +[source, cypher] +---- +CREATE OR REPLACE DATABASE neo4j +---- + +For more information, see the link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/standard-databases/create-databases/#_create_databases_with_if_not_exists_or_or_replace[Operations Manual -> Create databases with `IF NOT EXISTS` or `OR REPLACE`]. \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/finish.adoc b/modules/ROOT/pages/clauses/finish.adoc index b233b4896..2d3315e13 100644 --- a/modules/ROOT/pages/clauses/finish.adoc +++ b/modules/ROOT/pages/clauses/finish.adoc @@ -3,17 +3,19 @@ [[query-finish]] = FINISH -A query ending in `FINISH` — instead of `RETURN` — has no result but executes all its side effects. +A query ending in `FINISH` -- instead of `RETURN` -- has no result but executes all its side effects. `FINISH` was introduced as part of Cypher's xref:appendix/gql-conformance/index.adoc[]. The following read query successfully executes but has no results: .Query +// tag::clauses_finish_match[] [source, cypher] ---- MATCH (p:Person) FINISH ---- +// end::clauses_finish_match[] The following query has no result but creates one node with the label `Person`: diff --git a/modules/ROOT/pages/clauses/foreach.adoc b/modules/ROOT/pages/clauses/foreach.adoc index b594ecace..2132954e6 100644 --- a/modules/ROOT/pages/clauses/foreach.adoc +++ b/modules/ROOT/pages/clauses/foreach.adoc @@ -33,15 +33,17 @@ CREATE [[foreach-mark-all-nodes-along-a-path]] == Mark all nodes along a path -This query will set the property `marked` to `true` on all nodes along a path. +This query sets the property `marked` to `true` on all nodes along a path. .Query +// tag::clauses_foreach_node[] [source, cypher, indent=0] ---- MATCH p=(start)-[*]->(finish) WHERE start.name = 'A' AND finish.name = 'D' FOREACH (n IN nodes(p) | SET n.marked = true) ---- +// end::clauses_foreach_node[] .Result [role="queryresult",options="footer",cols="1*(finish) +WHERE start.name = 'A' AND finish.name = 'D' +FOREACH ( r IN relationships(p) | SET r.marked = true ) +---- +// end::clauses_foreach_relationship[] + +.Result +[role="queryresult",options="footer",cols="1* Database administration] -* link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/[Operations Manual -> Authentication and authorization] -* link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/[Operations Manual -> Clustering] +* link:{neo4j-docs-base-uri}/operations-manual/current/database-administration[Operations Manual -> Database administration] +* link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/[Operations Manual -> Authentication and authorization] +* link:{neo4j-docs-base-uri}/operations-manual/current/clustering/[Operations Manual -> Clustering] diff --git a/modules/ROOT/pages/clauses/limit.adoc b/modules/ROOT/pages/clauses/limit.adoc index cc29cf861..87646000f 100644 --- a/modules/ROOT/pages/clauses/limit.adoc +++ b/modules/ROOT/pages/clauses/limit.adoc @@ -16,7 +16,7 @@ The only clause that guarantees a specific row order is xref:clauses/order-by.ad The following graph is used for the examples below: -image::graph_limit_clause.svg[width="600", role="middle"] +image::graph-limit-clause.svg[Example graph connecting Person nodes,width=500,role=popup] To recreate it, run the following query against an empty Neo4j database: @@ -166,12 +166,14 @@ Properties set: 1 `LIMIT` can be used as a standalone clause, or in conjunction with xref:clauses/order-by.adoc[`ORDER BY`] or xref:clauses/skip.adoc[`SKIP`]/xref:clauses/skip.adoc#offset-synonym[`OFFSET`]. .Standalone use of `LIMIT` +// tag::clauses_limit_standalone[] [source, cypher] ---- MATCH (n) LIMIT 2 RETURN collect(n.name) AS names ---- +// end::clauses_limit_standalone[] .Result [role="queryresult",options="header,footer",cols="1* m| rolesBoostedExecution a| List of roles permitted to use boosted mode when executing this function. -Is `null` without the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Is `null` without the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. m| LIST m| isDeprecated @@ -81,7 +81,7 @@ m| STRING [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]. +More details about the syntax descriptions can be found link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/syntax/#administration-syntax-reading[here]. ==== List functions, either all or only built-in or user-defined:: @@ -124,7 +124,7 @@ SHOW [ALL|BUILT IN|USER DEFINED] FUNCTION[S] EXECUTABLE BY username [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- -Required privilege link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration#access-control-dbms-administration-user-management[`SHOW USER`]. +Required privilege link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration#access-control-dbms-administration-user-management[`SHOW USER`]. This command cannot be used for LDAP users. [NOTE] @@ -363,7 +363,7 @@ SHOW FUNCTIONS EXECUTABLE BY CURRENT USER YIELD * 6+d|Rows: 10 |=== -Notice that the two `roles` columns are empty due to missing the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Notice that the two `roles` columns are empty due to missing the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. Also note that the following columns are not present in the table: * `signature` diff --git a/modules/ROOT/pages/clauses/listing-procedures.adoc b/modules/ROOT/pages/clauses/listing-procedures.adoc index b79eba881..ed422d1c2 100644 --- a/modules/ROOT/pages/clauses/listing-procedures.adoc +++ b/modules/ROOT/pages/clauses/listing-procedures.adoc @@ -53,13 +53,13 @@ m| BOOLEAN m| rolesExecution a| List of roles permitted to execute this procedure. -Is `null` without the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Is `null` without the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. m| LIST m| rolesBoostedExecution a| List of roles permitted to use boosted mode when executing this procedure. -Is `null` without the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Is `null` without the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. m| LIST m| isDeprecated @@ -84,7 +84,7 @@ The deprecation information for procedures is returned both in the `isDeprecated [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]. +More details about the syntax descriptions can be found link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/syntax/#administration-syntax-reading[here]. ==== List all procedures:: @@ -127,7 +127,7 @@ SHOW PROCEDURE[S] EXECUTABLE BY username [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- -Requires the privilege link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration#access-control-dbms-administration-user-management[`SHOW USER`]. +Requires the privilege link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration#access-control-dbms-administration-user-management[`SHOW USER`]. This command cannot be used for LDAP users. [NOTE] @@ -246,7 +246,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 link:{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/current/reference/procedures#/#_list_of_procedures[Operations Manual -> List of procedures]. == Listing procedures with filtering on output columns @@ -285,7 +285,7 @@ WHERE admin |=== 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]. +For a full list of all procedures which require `admin` privileges in Neo4j, visit the link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures#/#_list_of_procedures[Operations Manual -> List of procedures]. == Listing procedures with other filtering @@ -401,7 +401,7 @@ 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 link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Note that the two `roles` columns are empty due to missing the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. Also note that the following columns are not present in the table: * `mode` @@ -416,7 +416,7 @@ Also note that the following columns are not present in the table: 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 in the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration/#access-control-dbms-administration-execute[Operations Manual -> The `DBMS EXECUTE` privileges]). +(More information about `DBMS EXECUTE` privilege administration can be found in the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-execute[Operations Manual -> The `DBMS EXECUTE` privileges]). .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 df859d1cb..b29ee66ca 100644 --- a/modules/ROOT/pages/clauses/listing-settings.adoc +++ b/modules/ROOT/pages/clauses/listing-settings.adoc @@ -37,7 +37,7 @@ 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]. +For dynamically updating a setting value, see link:{neo4j-docs-base-uri}/operations-manual/current/configuration/dynamic-settings/[Update dynamic settings]. label:default-output[] m| BOOLEAN @@ -73,7 +73,7 @@ m| BOOLEAN [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]. +More details about the syntax descriptions can be found link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/syntax/#administration-syntax-reading[here]. ==== List settings:: @@ -174,19 +174,19 @@ SHOW SETTINGS |=== The above table only displays the first 10 results of the query. -For a full list of all available settings in Neo4j, refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings[Configuration settings]. +For a full list of all available settings in Neo4j, refer to link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings[Configuration settings]. == 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 the first three settings starting with 'dbms': +For example, the following query returns the name, value, and description of the first three settings starting with 'server': .Query [source, cypher] ---- SHOW SETTINGS YIELD name, value, description -WHERE name STARTS WITH 'dbms' +WHERE name STARTS WITH 'server' RETURN name, value, description LIMIT 3 ---- @@ -196,17 +196,17 @@ LIMIT 3 |=== | name | value | description -| "dbms.cluster.catchup.client_inactivity_timeout" -| "10m" -| "The catchup protocol times out if the given duration elapses with no network activity. Every message received by the client from the server extends the timeout duration." +| "server.backup.enabled" +| "true" +| "Enable support for running online backups." -| "dbms.cluster.discovery.endpoints" -| null -| "A comma-separated list of endpoints that a server should contact in order to discover other cluster members. Typically, all cluster members, including the current server, must be specified in this list. The setting configures the endpoints for Discovery service V1." +| "server.backup.exec_connector.command" +| "" +| "Command to execute for ExecDataConnector list" -| "dbms.cluster.discovery.log_level" -| "WARN" -| "The level of middleware logging." +| "server.backup.exec_connector.scheme" +| "" +| "Schemes ExecDataConnector will match on" 3+d|Rows: 3 |=== diff --git a/modules/ROOT/pages/clauses/load-csv.adoc b/modules/ROOT/pages/clauses/load-csv.adoc index de374e266..4cb1bf357 100644 --- a/modules/ROOT/pages/clauses/load-csv.adoc +++ b/modules/ROOT/pages/clauses/load-csv.adoc @@ -22,7 +22,7 @@ Local paths are resolved relative to the Neo4j installation folder. [NOTE] ==== -Loading CSV files requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/load-privileges/[load privileges]. +Loading CSV files requires link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/load-privileges/[load privileges]. ==== == Import CSV data into Neo4j @@ -46,12 +46,14 @@ By default, paths are resolved relative to the Neo4j import directory. ---- .Query +// tag::clauses_load_csv_local_files[] [source, cypher] ---- LOAD CSV FROM 'file:///artists.csv' AS row MERGE (a:Artist {name: row[1], year: toInteger(row[2])}) RETURN a.name, a.year ---- +// end::clauses_load_csv_local_files[] .Result [role="queryresult",options="header,footer",cols="2* Load a dump from a cloud storage] on how to set up access to cloud storages. + [[azure-cloud-storage]] [role=label--new-5.24] ==== Import from an Azure Cloud Storage URI @@ -291,6 +303,53 @@ Added 4 nodes, Set 8 properties, Added 4 labels |=== ==== +[role=label--new-5.26] +[[dynamic-columns]] +=== Import CSV files using dynamic columns + +CSV columns can be referenced dynamically to map labels to nodes in the graph. +This enables flexible data handling, allowing labels to be be populated from CSV column values without manually specifying each entry. +It also mitigates the risk of Cypher injection. +(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]). + +.bands-with-headers.csv +[source, csv, filename="bands-with-headers.csv"] +---- +Id,Label,Name +1,Band,The Beatles +2,Band,The Rolling Stones +3,Band,Pink Floyd +4,Band,Led Zeppelin +---- + +.Query +// tag::clauses_load_csv_dynamic_columns[] +[source, cypher] +---- +LOAD CSV WITH HEADERS FROM 'file:///bands-with-headers.csv' AS line +MERGE (n:$(line.Label) {name: line.Name}) +RETURN n AS bandNodes +---- +// end::clauses_load_csv_dynamic_columns[] + +.Result +[role="queryresult",options="header,footer",cols="1* Performance caveats]. === Import compressed CSV files @@ -601,12 +660,12 @@ For more `STRING` manipulation functions, see xref:functions/string.adoc[String == Recommendations -=== Create uniqueness constraints +=== Create property uniqueness constraints -Always create uniqueness xref:constraints/index.adoc[constraints] prior to importing data, to avoid duplicates or colliding entities. +Always create xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[property uniqueness constraints] prior to importing data, to avoid duplicates or colliding entities. If the source file contains duplicated data and the right constraints are in place, Cypher raises an error. -.Create xref:constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints] on person ID +.Create a node property uniqueness constraints on person ID ==== .persons.csv @@ -650,10 +709,11 @@ person_tmdbId,bio,born,bornIn,died,person_imdbId,name,person_poster,person_url ---- [NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +The query below uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. .Query +// tag::clauses_load_csv_transactions[] [source, cypher] ---- LOAD CSV WITH HEADERS FROM 'https://data.neo4j.com/importing-cypher/persons.csv' AS row @@ -662,6 +722,7 @@ CALL (row) { SET p.name = row.name, p.born = row.born } IN TRANSACTIONS OF 200 ROWS ---- +// end::clauses_load_csv_transactions[] .Result [source, role="queryresult"] @@ -696,11 +757,13 @@ A common use case for this function is to generate sequential unique IDs for CSV ---- .Query +// tag::clauses_load_csv_linenumber[] [source, cypher] ---- LOAD CSV FROM 'file:///artists.csv' AS row RETURN linenumber() AS number, row ---- +// end::clauses_load_csv_linenumber[] .Result [role="queryresult",options="header,footer",cols="2*(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), - (martin)-[:FATHER_OF]->(charlie) +CREATE (charlie:Person:Actor {name: 'Charlie Sheen'}), + (martin:Person:Actor {name: 'Martin Sheen'}), + (michael:Person:Actor {name: 'Michael Douglas'}), + (oliver:Person:Director {name: 'Oliver Stone'}), + (rob:Person:Director {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) ---- +[[find-nodes]] +== Find nodes -[[basic-node-finding]] -== Basic node finding +The `MATCH` clause allows you to specify node patterns of varying complexity to retrieve from a graph. +For more information about finding node patterns, see xref:patterns/fixed-length-patterns#node-patterns[Patterns -> Node patterns]. -[[get-all-nodes]] -=== Get all nodes +[[find-all-nodes]] +=== Find all nodes By specifying a pattern with a single node and no labels, all nodes in the graph will be returned. -.Query -[source, cypher, indent=0] +.Find all nodes in a graph +// tag::clauses_match_all_nodes[] +[source, cypher] ---- MATCH (n) RETURN n ---- - -Returns all the nodes in the database. +// end::clauses_match_all_nodes[] .Result [role="queryresult",options="header,footer",cols="1* +| "Martin Sheen" | +| "Michael Douglas" | +| "Oliver Stone" | +| "Rob Reiner" | +| | "Wall Street" +| | "The American President" +2+d| Rows: 7 |=== - -[[match-with-labels]] -=== Match with labels - -To constrain a pattern with labels on nodes, add the labels to the nodes in the pattern. - -.Query -[source, cypher, indent=0] +.Node pattern using negation (`!`) label expression +// tag::clauses_match_label_expression_negation[] +[source, cypher] ---- -MATCH (:Person {name: 'Oliver Stone'})--(movie:Movie) -RETURN movie.title +MATCH (n:!Movie) +RETURN labels(n) AS label, count(n) AS labelCount ---- +// end::clauses_match_label_expression_negation[] -Returns any nodes with the `Movie` label connected to `Oliver Stone`. + +[NOTE] +The above query uses the xref:functions/list.adoc#functions-labels[`labels()`] and xref:functions/aggregating.adoc#functions-count[`count()`] functions. .Result -[role="queryresult",options="header,footer",cols="1* Label expressions]. -[[label-expression-match-or-expression]] -=== Match with a label expression for the node labels +[[find-relationships]] +== Find relationships -A match with an `OR` expression for the node label returns the nodes that contains both the specified labels. +The `MATCH` clause allows you to specify relationship patterns of varying complexity to retrieve from a graph. +Unlike a node pattern, a relationship pattern cannot be used in a `MATCH` clause without node patterns at both ends. +For more information about relationship patterns, see xref:patterns/fixed-length-patterns#relationship patterns[Patterns -> Relationship patterns]. -.Query +[NOTE] +Relationships will only be matched once inside a single pattern. +Read more about this behavior in the section on xref::patterns/reference.adoc#graph-patterns-rules-relationship-uniqueness[relationship uniqueness]. + +[[empty-relationship-patterns]] +=== Empty relationship patterns + +By applying `--`, a pattern will be matched for a relationship with any direction and without any filtering on relationship types or properties. + +.Find connected nodes using an empty relationship pattern [source, cypher] ---- -MATCH (n:Movie|Person) -RETURN n.name AS name, n.title AS title +MATCH (:Person {name: 'Oliver Stone'})--(n) +RETURN n AS connectedNodes ---- .Result -[role="queryresult",options="header,footer",cols="2* -| "Martin Sheen" | -| "Michael Douglas" | -| "Oliver Stone" | -| "Rob Reiner" | -| | "Wall Street" -| | "The American President" -2+|Rows: 7 +| connectedNodes +| (:Movie {title: "Wall Street"}) +1+d| Rows: 1 |=== +[[directed-relationship-patterns]] +=== Directed relationship patterns -[[relationship-basics]] -== Relationship basics - -[[outgoing-relationships]] -=== Outgoing relationships +The direction of a relationship in a pattern is indicated by arrows: `+-->+` or `+<--+`. -When the direction of a relationship is of interest, it is shown by using `-->` or `<--`. -For example: - -.Query -[source, cypher, indent=0] +.Find all nodes connected to `Oliver Stone` by an outgoing relationship. +[source, cypher] ---- -MATCH (:Person {name: 'Oliver Stone'})-->(movie) -RETURN movie.title +MATCH (:Person {name: 'Oliver Stone'})-->(movie:Movie) +RETURN movie.title AS movieTitle ---- -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) +MATCH (:Person {name: 'Oliver Stone'})-[r]->() +RETURN type(r) AS relType ---- +// end::clauses_match_relationship_types[] -Returns the type of each outgoing relationship from `Oliver Stone`. +[NOTE] +The above query uses the xref:functions/scalar.adoc#functions-type[`type()` function]. .Result -[role="queryresult",options="header,footer",cols="1*()` will never match a relationship. + +For a list of all relationship type expressions supported by Cypher, see xref:patterns/reference.adoc#label-expressions[Patterns -> Label expressions]. + +[[multiple-relationships]] +=== Find multiple relationships + +A graph pattern can contain several relationship patterns. + +.Graph pattern including several relationship patterns +[source, cypher] +---- +MATCH (:Person {name: 'Charlie Sheen'})-[:ACTED_IN]->(movie:Movie)<-[:DIRECTED]-(director:Person) +RETURN movie.title AS movieTitle, director.name AS director +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(movie:Movie) +WHERE charlie.name = 'Charlie Sheen' +RETURN movie.title AS movieTitle +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(movie:Movie) +WHERE martin.name = 'Martin Sheen' AND NOT EXISTS { + MATCH (movie)<-[:DIRECTED]-(director:Person {name: 'Oliver Stone'}) +} +RETURN movie.title AS movieTitle +---- + +[NOTE] +The above query uses an xref:subqueries/existential.adoc[`EXISTS` subquery]. + +.Result +[role="queryresult",options="header,footer",cols="1* Parameters]. -[[match-on-rel-type-use-variable]] -=== Match on relationship type and use a variable +[[find-paths]] +== Find paths -Variables and specific relationship types can be included in the same pattern. -For example: +The `MATCH` clause can also be used to bind whole paths to variables. -.Query -[source, cypher, indent=0] +.Find all paths matching a pattern +// tag::clauses_match_path[] +[source, cypher] ---- -MATCH (wallstreet {title: 'Wall Street'})<-[r:ACTED_IN]-(actor) -RETURN r.role +MATCH path = ()-[:ACTED_IN]->(movie:Movie) +RETURN path ---- +// end::clauses_match_path[] + +.Result +[role="queryresult",options="header,footer",cols="1*(:Movie {title: "Wall Street"}) +| (:Person {name: "Martin Sheen"})-[:ACTED_IN {role: "Carl Fox"}]->(:Movie {title: "Wall Street"}) +| (:Person {name: "Martin Sheen"})-[:ACTED_IN {role: "A.J. MacInerney"}]->(:Movie {title: "The American President"}) +| (:Person {name: "Michael Douglas"})-[:ACTED_IN {role: "Gordon Gekko"}]->(:Movie {title: "Wall Street"}) +| (:Person {name: "Michael Douglas"})-[:ACTED_IN {role: "President Andrew Shepherd"}]->(:Movie {title: "The American President"}) +1+d| Rows: 5 +|=== -Returns the `ACTED_IN` roles for the movie `Wall Street`. + +.Find paths matching a pattern including a `WHERE` predicate +[source, cypher] +---- +MATCH path = (:Person)-[:ACTED_IN]->(movie:Movie)<-[:DIRECTED]-(:Person) +WHERE movie.title = 'Wall Street' +RETURN path +---- .Result [role="queryresult",options="header,footer",cols="1*(:Movie {title: "Wall Street"})<-[:DIRECTED]-(:Person {name: "Oliver Stone"}) +| (:Person {name: "Martin Sheen"})-[:ACTED_IN {role: "Carl Fox"}]->(:Movie {title: "Wall Street"})<-[:DIRECTED]-(:Person {name: "Oliver Stone"}) +| (:Person {name: "Michael Douglas"})-[:ACTED_IN {role: "Gordon Gekko"}]->(:Movie {title: "Wall Street"})<-[:DIRECTED]-(:Person {name: "Oliver Stone"}) +1+d| Rows: 3 +|=== + +For more information about how `MATCH` is used to find patterns of varying complexity (including xref:patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path patterns], xref:patterns/variable-length-patterns.adoc#quantified-relationships[quantified relationships], and the xref:patterns/shortest-paths.adoc[shortest paths] between nodes), see the section on xref::patterns/index.adoc[Patterns]. + +== Multiple MATCH clauses, the WITH clause, and clause composition + +In Cypher, the behavior of a query is defined by its clauses. +Each clause takes the current graph state and a table of intermediate results, processes them, and passes the updated graph state and results to the next clause. +The first clause starts with the graph's initial state and an empty table, while the final clause produces the query result. + +.Chaining consecutive `MATCH` clauses +[source, cypher] +---- +MATCH (:Person {name: 'Martin Sheen'})-[:ACTED_IN]->(movie:Movie) // <1> +MATCH (director:Person)-[:DIRECTED]->(movie) // <2> +RETURN director.name AS director, movie.title AS movieTitle +---- +<1> The result of the first `MATCH` clause is the variable `movie` which holds all the `Movies` that `Martin Sheen` has `ACTED_IN`. +<2> The second `MATCH` clause uses the `movie` variable to find any `Person` node with a `DIRECTED` relationship to those `Movie` nodes that `Martin Sheen` has `ACTED_IN`. + +.Result +[role="queryresult",options="header,footer",cols="2*(movies:Movie) // <1> +WITH actors, count(movies) AS movieCount // <2> +ORDER BY movieCount DESC +LIMIT 1 // <3> +MATCH (actors)-[:ACTED_IN]->(movies) // <4> +RETURN actors.name AS actor, movieCount, collect(movies.title) AS movies +---- +<1> The `Person` and `Movie` nodes matched in this step are stored in variables, which are then passed on to the second row of the query. +<2> The `movies` variable is implicitly imported by its occurrence in the `count()` function. +The `WITH` clause explicitly imports the `actors` variable. +<3> An xref:clauses/order-by.adoc[`ORDER BY`] clause orders the results by `movieCount` in descending order, ensuring that the `Person` with the highest number of movies appears at the top, and xref:clauses/limit.adoc[`LIMIT] 1` ensures that all other `Person` nodes are discarded. +<4> The second `MATCH` clause finds all `Movie` nodes associated with the `Person` nodes currently bound to the `actors` variable. [NOTE] -==== -Relationships will only be matched once inside a single pattern. -Read more about this in the section on xref::patterns/reference.adoc#graph-patterns-rules-relationship-uniqueness[relationship uniqueness]. -==== +The above query uses the xref:functions/aggregating.adoc#functions-collect[`collect()` function]. -[[rel-types-with-uncommon-chars]] -=== Relationship types with uncommon characters +.Result +[role="queryresult",options="header,footer",cols="3* Protecting against Cypher injection]). + +.Syntax for matching node labels dynamically +[source, syntax] ---- -MATCH - (martin:Person {name: 'Martin Sheen'}), - (rob:Person {name: 'Rob Reiner'}) -CREATE (rob)-[:`OLD FRIENDS`]->(martin) +MATCH (n:$()) +MATCH (n:$any()) +MATCH (n:$all()) ---- -This leads to the following graph: +[NOTE] +`MATCH (n:$all())` is functionally equivalent to `MATCH (n:$())`. + +.Syntax for matching relationship types dynamically +[source, syntax] +---- +MATCH ()-[r:$())]->() +MATCH ()-[r:$any()]->() +MATCH ()-[r:$all())]->() +---- -image::graph_match_clause_backtick.svg[width="600", role="middle"] +The expression must evaluate to a `STRING NOT NULL | LIST NOT NULL` value. +If you use a `LIST` with more than one item in a relationship pattern with dynamic relationship types, no results will be returned. +This is because a relationship can only have exactly one type. -.Query -[source, cypher, indent=0] +.Match labels dynamically +[source, cypher] ---- -MATCH (n {name: 'Rob Reiner'})-[r:`OLD FRIENDS`]->() -RETURN type(r) +WITH ["Person", "Director"] AS labels +MATCH (directors:$(labels)) +RETURN directors ---- .Result [role="queryresult",options="header,footer",cols="1*(movie)<-[:DIRECTED]-(director) -RETURN movie.title, director.name +{ + "label": "Movie" +} ---- -Returns the movie in which `Charlie Sheen` acted and its director. +.Match nodes dynamically using a parameter +// tag::clauses_match_dynamic_match_parameter[] +[source, cypher] +---- +MATCH (movie:$($label)) +RETURN movie.title AS movieTitle +---- +// end::clauses_match_dynamic_match_parameter[] + + +.Result +[role="queryresult",options="header,footer",cols="1*() +RETURN relationshipType, count(r) AS relationshipCount +---- +// end::clauses_match_dynamic_match_variable[] + .Result [role="queryresult",options="header,footer",cols="2* Protecting against Cypher injection]). + +.Syntax for merging nodes and relationships dynamically +[source, syntax] +---- +MERGE (n:$()) +MERGE ()-[r:$()]->() +---- + +The expression must evaluate to a `STRING NOT NULL | LIST NOT NULL` value. +Using a `LIST` with more than one item when merging a relationship using dynamic relationship types will fail. +This is because a relationship can only have exactly one type. + +.Parameters +[source, parameters] +---- +{ + "nodeLabels": ["Person", "Director"], + "relType": "DIRECTED", + "movies": ["Ladybird", "Little Women", "Barbie"] +} +---- + +.Merge nodes and relationships using dynamic node labels and relationship types +// tag::clauses_merge_dynamic_merge[] +[source, cypher] +---- +MERGE (greta:$($nodeLabels) {name: 'Greta Gerwig'}) +WITH greta +UNWIND $movies AS movieTitle +MERGE (greta)-[rel:$($relType)]->(m:Movie {title: movieTitle}) +RETURN greta.name AS name, labels(greta) AS labels, type(rel) AS relType, collect(m.title) AS movies +---- +// end::clauses_merge_dynamic_merge[] + +.Result +[role="queryresult",options="footer",cols="4*() RETURN p.name, r ---- +// end::clauses_optional_match[] .Result [role="queryresult",options="header,footer",cols="2* Protecting against Cypher injection]). [source, syntax] ---- @@ -82,7 +84,6 @@ REMOVE n[key] The dynamically calculated key must evaluate to a `STRING` value. This query creates a copy of every property on the nodes: - .Query [source, cypher, indent=0] ---- @@ -131,18 +132,19 @@ Labels removed: 1 |=== [role=label--new-5.24] -[[remove-remove-a-label-dynamically-from-a-node]] -== Dynamically removing a label +[[dynamic-remove-node-label]] +== Dynamically remove a node label `REMOVE` can be used to remove a label on a node even when the label is not statically known. - [source, syntax] ---- MATCH (n) REMOVE n:$(expr) ---- +The expression must evaluate to a `STRING NOT NULL | LIST NOT NULL` value. + .Query [source, cypher, indent=0] ---- diff --git a/modules/ROOT/pages/clauses/return.adoc b/modules/ROOT/pages/clauses/return.adoc index 323fc40cd..f62810787 100644 --- a/modules/ROOT/pages/clauses/return.adoc +++ b/modules/ROOT/pages/clauses/return.adoc @@ -32,11 +32,13 @@ CREATE To return a node, list it in the `RETURN` clause: .Query +// tag::clauses_return_node[] [source, cypher] ---- MATCH (p:Person {name: 'Keanu Reeves'}) RETURN p ---- +// end::clauses_return_node[] .Result [role="queryresult",options="header,footer",cols="1*(m) RETURN type(r) ---- +// end::clauses_return_relationship_type[] .Result [role="queryresult",options="header,footer",cols="1*(m) RETURN * ---- +// end::clauses_return_all_elements[] This returns the two nodes, and the two possible paths between them. @@ -149,11 +157,13 @@ d|Rows: 1 Names of returned columns can be renamed using the `AS` operator: .Query +// tag::clauses_return_with_column_alias[] [source, cypher] ---- MATCH (p:Person {name: 'Keanu Reeves'}) RETURN p.nationality AS citizenship ---- +// end::clauses_return_with_column_alias[] Returns the `nationality` property of `'Keanu Reeves'`, but the column is renamed to `citizenship`. @@ -220,11 +230,13 @@ Returns a predicate, a literal and function call with a pattern expression param `DISTINCT` retrieves only unique rows for the columns that have been selected for output. .Query +// tag::clauses_return_distinct[] [source, cypher] ---- MATCH (p:Person {name: 'Keanu Reeves'})-->(m) RETURN DISTINCT m ---- +// end::clauses_return_distinct[] 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'`): diff --git a/modules/ROOT/pages/clauses/set.adoc b/modules/ROOT/pages/clauses/set.adoc index 6976c8abe..92f489482 100644 --- a/modules/ROOT/pages/clauses/set.adoc +++ b/modules/ROOT/pages/clauses/set.adoc @@ -17,7 +17,8 @@ The query statistics will state whether any updates actually took place. The following graph is used for the examples below: -image::graph_set_clause.svg[[width="500", role="middle"] +image::graph-set-clause.svg[Example graph connecting people through know relationships,width=300,role=popup] +// Another exception here, a graph that is small but "wide" and so might look better with a smaller width so it doesn't occupy too much space. To recreate it, run the following query against an empty Neo4j database: @@ -162,10 +163,12 @@ Properties set: 1 [role=label--new-5.24] -[[set-dynamically-a-property]] -== Dynamically setting or updating a property +[[dynamic-set-property]] +== Dynamically set or update a property `SET` can be used to set or update a property on a node or relationship even when the property key name is not statically known. +This allows for more flexible queries and mitigates the risk of Cypher injection. +(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]). [source, syntax] ---- @@ -541,15 +544,15 @@ Labels added: 1 |=== [role=label--new-5.24] -[[set-set-a-dynamic-label-on-a-node]] -== Dynamically setting a label +[[dynamic-set-node-label]] +== Dynamically set a node label `SET` can be used to set a label on a node even when the label is not statically known. [source, syntax] ---- MATCH (n) -SET n:$(expr) +SET n:$() ---- .Query @@ -629,7 +632,7 @@ Labels added: 2 |=== [role=label--new-5.24] -[[set-set-multiple-dynamic-labels-on-a-node]] +[[dynamic-set-multiple-node-labels]] == Set multiple labels dynamically on a node It is possible to set multiple labels dynamically using a `LIST` and/or by chaining them separately with a `:`: diff --git a/modules/ROOT/pages/clauses/skip.adoc b/modules/ROOT/pages/clauses/skip.adoc index 2f31a9f49..47961fd50 100644 --- a/modules/ROOT/pages/clauses/skip.adoc +++ b/modules/ROOT/pages/clauses/skip.adoc @@ -69,6 +69,7 @@ d|Rows: 2 The following query returns the middle two rows, with `SKIP` skipping the first and xref:clauses/limit.adoc[`LIMIT`] removing the final two. .Query +// tag::clauses_skip[] [source, cypher] ---- MATCH (n) @@ -77,6 +78,7 @@ ORDER BY n.name SKIP 1 LIMIT 2 ---- +// end::clauses_skip[] .Result [role="queryresult",options="header,footer",cols="1* Importing variables]. + .Query [source, cypher] ---- -CALL { +CALL () { MATCH (a:Actor) RETURN a.name AS name UNION ALL diff --git a/modules/ROOT/pages/clauses/unwind.adoc b/modules/ROOT/pages/clauses/unwind.adoc index e2c3afc4a..55a739fd2 100644 --- a/modules/ROOT/pages/clauses/unwind.adoc +++ b/modules/ROOT/pages/clauses/unwind.adoc @@ -29,11 +29,13 @@ The `UNWIND` clause requires you to specify a new name for the inner values. We want to transform the literal list into rows named `x` and return them. .Query +// tag::clauses_unwind_list[] [source, cypher] ---- UNWIND [1, 2, 3, null] AS x RETURN x, 'val' AS y ---- +// end::clauses_unwind_list[] Each value of the original list -- including `null` -- is returned as an individual row. @@ -109,6 +111,7 @@ The two lists -- _a_ and _b_ -- are concatenated to form a new list, which is th Multiple `UNWIND` clauses can be chained to unwind nested list elements. .Query +// tag::clauses_unwind_nested_list[] [source, cypher] ---- WITH [[1, 2], [3, 4], 5] AS nested @@ -116,6 +119,7 @@ UNWIND nested AS x UNWIND x AS y RETURN y ---- +// end::clauses_unwind_nested_list[] The first `UNWIND` results in three rows for `x`, each of which contains an element of the original list (two of which are also lists); namely, `[1, 2]`, `[3, 4]`, and `5`. The second `UNWIND` then operates on each of these rows in turn, resulting in five rows for `y`. @@ -212,6 +216,7 @@ Create a number of nodes and relationships from a parameter-list without using ` ---- .Query +// tag::clauses_unwind_create_nodes[] [source, cypher] ---- UNWIND $events AS event @@ -219,6 +224,7 @@ MERGE (y:Year {year: event.year}) MERGE (y)<-[:IN]-(e:Event {id: event.id}) RETURN e.id AS x ORDER BY x ---- +// end::clauses_unwind_create_nodes[] Each value of the original list is unwound and passed through `MERGE` to find or create the nodes and relationships. diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 20df616ca..1e79b1efa 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}/database-administration/composite-databases/concepts/[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/current/database-administration/composite-databases/concepts/[composite database], the `USE` clause can also appear as the first clause of: * Union parts: + @@ -41,13 +41,14 @@ USE + [source, syntax, role="noheader"] ---- -CALL { +CALL () { USE } ---- + -In subqueries, a `USE` clause may appear as the second clause, if directly following an xref::subqueries/call-subquery.adoc#call-importing-variables[importing `WITH` clause]. +In subqueries, a `USE` clause may appear directly following the xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause]: `CALL () { ... }` (introduced in Neo4j 5.23). +Or, if you are using an older version of Neo4j, directly following an xref::subqueries/call-subquery.adoc#importing-with[importing `WITH` clause]. When executing queries against a composite database, the `USE` clause must only refer to graphs that are part of the current composite database. @@ -67,14 +68,16 @@ CREATE ALIAS `myComposite`.`myConstituent` FOR DATABASE `myDatabase`; [[query-use-examples-query-graph]] === Query a graph -In this example it is assumed that the DBMS contains a database named `myDatabase`: +This example assumes that the DBMS contains a database named `myDatabase`: .Query +// tag::clauses_use[] [source, cypher] ---- USE myDatabase MATCH (n) RETURN n ---- +// end::clauses_use[] [[query-use-examples-query-composite-database-constituent-graph]] === Query a composite database constituent graph @@ -82,17 +85,19 @@ 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 +// tag::clauses_use_composite[] [source, cypher] ---- USE myComposite.myConstituent MATCH (n) RETURN n ---- +// end::clauses_use_composite[] [[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 xref:functions/graph.adoc#functions-graph-byname[`graph.byName()`] function 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`: @@ -113,12 +118,18 @@ USE graph.byName($graphName) MATCH (n) RETURN n ---- +[role=label--new-5.13] [[query-use-examples-query-composite-database-by-element-id]] === Query a composite database constituent using elementId -The `graph.byElementId()` function (introduced in Neo4j 5.13), can be used in the `USE` clause to resolve a constituent graph to which a given element id belongs. -In the below example, it is assumed that the DBMS contains a composite database constituent, which contains the element id `4:c0a65d96-4993-4b0c-b036-e7ebd9174905:0`. -If the constituent database is not a standard database in the DBMS an error will be thrown: +The xref:functions/graph.adoc#functions-graph-by-elementid[`graph.byElementId()`] function can be used in the `USE` clause to resolve a constituent graph to which a given xref:functions/scalar.adoc#functions-elementid[element id] belongs. +As of Neo4j 5.26, it is supported on both standard and composite databases (on previous versions it is only available on composite databases). + +[NOTE] +On a standard database, a `USE` clause with `graph.byElementId()` cannot be combined with other `USE` clauses unless the subsequent `USE` clauses reference the same element id. + +In the below example, it is assumed that the DBMS contains the database corresponding to the given element id. If you are connected to a composite database it needs to be a element id to a constituent database, which is a standard database in the DBMS. + .Query [source, cypher, role=test-skip] ---- diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 90334ffab..9a3bb0c25 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -46,12 +46,14 @@ CREATE `WHERE` can appear inside a node pattern in a `MATCH` clause or a pattern comprehension: .Query +// tag::clauses_where_in_match_clause[] [source, cypher] ---- WITH 30 AS minAge MATCH (a:Person WHERE a.name = 'Andy')-[:KNOWS]->(b:Person WHERE b.age > minAge) RETURN b.name ---- +// end::clauses_where_in_match_clause[] .Result [role="queryresult",options="header,footer",cols="1*(b WHERE b:Person) | b.name] AS friends ---- +// end::clauses_where_pattern_comprehension[] .Result [role="queryresult",options="header,footer",cols="1*(f) WHERE k.since < 2000 RETURN f.name, f.age, f.email ---- +// end::clauses_where_relationship_property[] The `name`, `age` and `email` values for `Peter` are returned because `Andy` has known him since before 2000: @@ -185,12 +197,19 @@ The `name`, `age` and `email` values for `Peter` are returned because `Andy` has To filter on a property using a dynamically computed name, use square bracket syntax: +.Parameters +[source, parameters] +---- +{ + "propname": "age" +} +---- + .Query [source, cypher] ---- -WITH 'AGE' AS propname MATCH (n:Person) -WHERE n[toLower(propname)] < 30 +WHERE n[$propname] < 30 RETURN n.name, n.age ---- @@ -211,12 +230,14 @@ The `name` and `age` values for `Timothy` are returned because he is less than 3 Use the `IS NOT NULL` predicate to only include nodes or relationships in which a property exists: .Query +// tag::clauses_where_property_existence[] [source, cypher] ---- MATCH (n:Person) WHERE n.belt IS NOT NULL RETURN n.name, n.belt ---- +// end::clauses_where_property_existence[] The `name` and `belt` values for `Andy` are returned because he is the only one with a `belt` property: @@ -235,6 +256,7 @@ The `name` and `belt` values for `Andy` are returned because he is the only one As `WHERE` is not considered a clause in its own right, its scope is not limited by a `WITH` directly before it. .Query +// tag::clauses_where_with[] [source, cypher] ---- MATCH (n:Person) @@ -242,6 +264,7 @@ WITH n.name as name WHERE n.age = 25 RETURN name ---- +// end::clauses_where_with[] .Result [role="queryresult",options="header,footer",cols="1*(timothy) RETURN other.name, other.age ---- +// end::clauses_where_patterns[] The `name` and `age` values for nodes that have an outgoing relationship to `Timothy` are returned: @@ -611,12 +637,14 @@ To check if an element exists in a list, use the `IN` operator. The below query checks whether a property exists in a literal list: .Query +// tag::clauses_where_lists[] [source, cypher] ---- MATCH (a:Person) WHERE a.name IN ['Peter', 'Timothy'] RETURN a.name, a.age ---- +// end::clauses_where_lists[] .Result [role="queryresult",options="header,footer",cols="2*(otherPerson) WITH *, type(r) AS connectionType RETURN person.name, otherPerson.name, connectionType ---- +// end::clauses_with_wildcard[] This query returns the names of all related persons and the type of relationship between them. diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc deleted file mode 100644 index e84df4470..000000000 --- a/modules/ROOT/pages/constraints/examples.adoc +++ /dev/null @@ -1,2939 +0,0 @@ -:description: Examples of how to manage constraints used for ensuring data integrity. -:page-toclevels: 1 -include::https://raw.githubusercontent.com/neo4j-graphacademy/courses/main/asciidoc/courses/cypher-indexes-constraints/ad.adoc[] - -[[constraints-examples]] -= Examples - -This page contains examples of how to manage constraints used for ensuring data integrity. - -[[constraints-examples-node-uniqueness]] -== Node property uniqueness constraints - -A node property uniqueness constraint ensures that certain nodes have a set of specified properties whose combined value is unique when all properties exist on the node. - -* xref::constraints/examples.adoc#constraints-create-a-node-uniqueness-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-node-uniqueness-constraint-by-param[] -* 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[] - - -[[constraints-create-a-node-uniqueness-constraint]] -=== Create a node property uniqueness constraint - -When creating a property uniqueness constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT book_isbn -FOR (book:Book) REQUIRE book.isbn IS UNIQUE ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view currently says `Unique constraints added: 1`. -It will be updated to say `Node property uniqueness constraints added: 1` in Neo4j 6.0. -==== - -====== - -[role=label--new-5.16] -[[constraints-create-a-node-uniqueness-constraint-by-param]] -=== Create a node property uniqueness constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "node_uniqueness_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR (book:Book) REQUIRE book.prop1 IS UNIQUE ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view currently says `Unique constraints added: 1`. -It will be updated to say `Node property uniqueness constraints added: 1` in Neo4j 6.0. -==== - -====== - - -[[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. -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. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -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 already exists, the query will return: - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view currently says `Unique constraints added: 1`. -It will be updated to say `Node property uniqueness constraints added: 1` in Neo4j 6.0. -==== - -====== - - -[[constraints-create-a-node-uniqueness-constraint-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] ----- -CREATE CONSTRAINT constraint_with_options -FOR (book:Book) REQUIRE (book.prop1, book.prop2) IS UNIQUE -OPTIONS { - indexProvider: 'range-1.0' -} ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view currently says `Unique constraints added: 1`. -It will be updated to say `Node property uniqueness constraints added: 1` in Neo4j 6.0. -==== - -====== - -There is no valid index configuration values for the constraint-backing range indexes. - - -[[constraints-create-an-already-existing-node-uniqueness-constraint]] -=== Creating an already existing constraint will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a property uniqueness constraint on the property `published` on nodes with the `Book` label, when that constraint already exists: - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT preExisting_book_published FOR (book:Book) REQUIRE book.published IS UNIQUE ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT book_published FOR (book:Book) REQUIRE book.published IS UNIQUE ----- - -In this case, the constraint cannot be created because it already exists. - -.Error message -[source, error] ----- -Constraint already exists: -Constraint( id=4, name='preExisting_book_published', type='UNIQUENESS', schema=(:Book {published}), ownedIndex=3 ) ----- - -[NOTE] -==== -The constraint type will be updated to say `NODE PROPERTY UNIQUENESS` in Neo4j 6.0. -==== - -====== - - -[[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 property uniqueness constraint on the property `wordCount` on nodes with the `Book` label, when an index already exists on that label and property combination: - -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX preExisting_book_word_count FOR (book:Book) ON (book.wordCount) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT book_word_count FOR (book:Book) REQUIRE book.wordCount IS UNIQUE ----- - -In this case, the constraint cannot be created because there already exists an index covering that schema. - -.Error message -[source, error] ----- -There already exists an index (:Book {wordCount}). -A constraint cannot be created until the index has been dropped. ----- - -====== - - -[[constraints-create-a-node-that-complies-with-a-uniqueness-constraint]] -=== Creating a node that complies with an existing constraint - - -.+CREATE NODE+ -====== - -Create a `Book` node with an `isbn` that is not already in the graph: - -.Query -[source, cypher] ----- -CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) ----- - -.Result -[queryresult] ----- -Added 1 label, created 1 node, set 2 properties ----- - -====== - - -[[constraints-create-a-node-that-violates-a-uniqueness-constraint]] -=== Creating a node that violates an existing constraint will fail - - -.+CREATE NODE+ -====== - -Create a `Book` node with an `isbn` that is already used in the graph: - -.Query -[source, cypher, role=test-fail] ----- -CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) ----- - -In this case, the node is not created because the `isbn` property is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Node(0) already exists with label `Book` and property `isbn` = '1449356265' ----- - -====== - - -[[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 property uniqueness constraint on the property `title` on nodes with the `Book` label, when there are two nodes with the same `title`: - -//// -[source, cypher, role=test-setup] ----- -CREATE (book:Book {isbn: '9780393972832', title: 'Moby Dick'}); -CREATE (book:Book {isbn: '9780763630188', title: 'Moby Dick'}) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -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/search-performance-indexes/managing-indexes.adoc[] instead, or remove/correct the offending nodes and then re-apply the constraint. - -.Error message -[source, error] ----- -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' ----- - -[NOTE] -==== -The constraint type will be updated to say `NODE PROPERTY UNIQUENESS` in Neo4j 6.0. -==== - -====== - -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 graph. -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 ----- - -[role=label--new-5.7] -[[constraints-examples-relationship-uniqueness]] -== Relationship property uniqueness constraints - -A relationship property uniqueness constraint ensures that certain relationships have a set of specified properties whose combined value is unique when all properties exist on the relationship. - -* xref::constraints/examples.adoc#constraints-create-a-relationship-uniqueness-constraints[] -* xref::constraints/examples.adoc#constraints-create-a-relationship-uniqueness-constraints-by-param[] -* 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[] - - -[[constraints-create-a-relationship-uniqueness-constraints]] -=== Create a relationship property uniqueness constraint - -When creating a property uniqueness constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT sequels -FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order, sequel.seriesTitle) IS UNIQUE ----- - -.Result -[queryresult] ----- -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 6.0. -==== - -====== - - -[role=label--new-5.16] -[[constraints-create-a-relationship-uniqueness-constraints-by-param]] -=== Create a relationship property uniqueness constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "rel_uniqueness_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.prop1) IS UNIQUE ----- - -.Result -[queryresult] ----- -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 6.0. -==== - -====== - - -[[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 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. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT sequels IF NOT EXISTS -FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order) IS UNIQUE ----- - -Assuming a constraint with the name `sequels` already exists: - -.Result -[queryresult] ----- -(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`. -It will be updated to say `Relationship property uniqueness constraints added: 1` in Neo4j 6.0. -==== - -====== - - -[[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] ----- -CREATE CONSTRAINT rel_constraint_with_options -FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order, sequel.seriesTitle, sequel.number) IS UNIQUE -OPTIONS { - indexProvider: 'range-1.0' -} ----- - -.Result -[queryresult] ----- -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 6.0. -==== - -====== - -There are no valid index configuration values for the constraint-backing range indexes. - - -[[constraints-create-an-already-existing-relationship-uniqueness-constraint]] -=== Creating an already existing constraint will fail - - -.+CREATE CONSTRAINT+ -====== - -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-fail] ----- -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. - -.Error message -[source, error] ----- -Constraint already exists: -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 6.0. -==== - -====== - - -[[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 `order` on relationships with the `SEQUEL_OF` relationship type, when an index already exists on that relationship type and property combination: - -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX sequel_order FOR ()-[sequel:SEQUEL_OF]-() ON (sequel.order) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -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. - -.Error message -[source, error] ----- -There already exists an index ()-[:SEQUEL_OF {order}]-(). -A constraint cannot be created until the index has been dropped. ----- - -====== - - -[[constraints-create-a-relationship-that-complies-with-a-uniqueness-constraint]] -=== Creating a relationship that complies with an existing constraint - - -.+CREATE RELATIONSHIP+ -====== - -Create a `SEQUEL_OF` relationship with values for properties `order` and `seriesTitle` that are not already in the graph: - -.Query -[source, cypher] ----- -CREATE (:Book {title: 'Spirit Walker'})-[:SEQUEL_OF {order: 1, seriesTitle: 'Chronicles of Ancient Darkness'}]->(:Book {title: 'Wolf Brother'}) ----- - -.Result -[queryresult] ----- -Added 2 labels, created 2 nodes, set 4 properties, created 1 relationship. ----- - -====== - - -[[constraints-create-a-relationship-that-violates-a-uniqueness-constraint]] -=== Creating a relationship that violates an existing constraint will fail - - -.+CREATE RELATIONSHIP+ -====== - -Create a `SEQUEL_OF` relationship with values for properties `order` and `seriesTitle` that are already used in the graph: - -.Query -[source, cypher, role=test-fail] ----- -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 because the combination of the `order` and `seriesTitle` properties are in conflict with an existing constraint. - -.Error message -[source, error] ----- -Relationship(0) already exists with type `SEQUEL_OF` and properties `order` = 1, `seriesTitle` = 'Chronicles of Ancient Darkness' ----- - -====== - - -[[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 `seriesTitle` on relationships with the `SEQUEL_OF` relationship type, when two relationships with the same `seriesTitle` already exist: - -//// -[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-fail] ----- -CREATE CONSTRAINT series_title FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.seriesTitle) IS UNIQUE ----- - -In this case, the constraint cannot be created because it is in conflict with the existing graph. -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] ----- -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 graph. -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=label--enterprise-edition] -[[constraints-examples-node-property-existence]] -== Node property existence constraints - -A node property existence constraint ensures that certain nodes have a specified property. - -* xref::constraints/examples.adoc#constraints-create-a-node-property-existence-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-node-property-existence-constraint-by-param[] -* 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[] - - -[[constraints-create-a-node-property-existence-constraint]] -=== Create a node property existence constraint - -When creating a node property existence constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT author_name -FOR (author:Author) REQUIRE author.name IS NOT NULL ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view for property existence constraints, `Property existence constraints added: 1`, will be split between nodes and relationships in Neo4j 6.0. -For the node property existence constraints, they will say `Node property existence constraints added: 1`. -==== - -====== - - -[role=label--new-5.16] -[[constraints-create-a-node-property-existence-constraint-by-param]] -=== Create a node property existence constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "node_exist_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR (author:Author) REQUIRE author.surname IS NOT NULL ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view for property existence constraints, `Property existence constraints added: 1`, will be split between nodes and relationships in Neo4j 6.0. -For the node property existence constraints, they will say `Node property existence constraints added: 1`. -==== - -====== - - -[[constraints-create-a-node-property-existence-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 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. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT author_pseudonym -FOR (author:Author) REQUIRE author.pseudonym IS UNIQUE ----- -//// - -.Query -[source, cypher] ----- -CREATE CONSTRAINT author_pseudonym IF NOT EXISTS -FOR (author:Author) REQUIRE author.pseudonym IS NOT NULL ----- - -Assuming a constraint with the name `author_pseudonym` already exists: - -.Result -[queryresult] ----- -(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. ----- - -====== - - -[[constraints-create-an-already-existing-node-property-existence-constraint]] -=== Creating an already existing constraint will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a node property existence constraint on the property `name` on nodes with the `Author` label, when that constraint already exists: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT author_name -FOR (author:Author) REQUIRE author.name IS NOT NULL ----- - -In this case, the constraint cannot be created because it already exists. - -.Error message -[source, error] ----- -An equivalent constraint already exists, 'Constraint( id=10, name='author_name', type='NODE PROPERTY EXISTENCE', schema=(:Author {name}) )'. ----- - -====== - - -[[constraints-create-a-node-that-complies-with-a-property-existence-constraint]] -=== Creating a node that complies with an existing constraint - - -.+CREATE NODE+ -====== - -Create an `Author` node with a `name` property: - -.Query -[source, cypher] ----- -CREATE (author:Author {name:'Virginia Woolf', surname: 'Woolf'}) ----- - -.Result -[queryresult] ----- -Added 1 label, created 1 node, set 2 properties ----- - -====== - - -[[constraints-create-a-node-that-violates-a-property-existence-constraint]] -=== Creating a node that violates an existing constraint will fail - - -.+CREATE NODE+ -====== - -Create an `Author` node without a `name` property, given a property existence constraint on `:Author(name)`: - -.Query -[source, cypher, role=test-fail] ----- -CREATE (author:Author {surname: 'Austen'}) ----- - -In this case, the node is not created because it is missing the `name` property which is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Node(0) with label `Author` must have the property `name` ----- - -====== - - -[[constraints-removing-an-existence-constrained-node-property]] -=== Removing an existence constrained node property will fail - - -.+REMOVE PROPERTY+ -====== - -Remove the `name` property from an existing node `Author`, given a property existence constraint on `:Author(name)`: - -.Query -[source, cypher, role=test-fail] ----- -MATCH (author:Author {name: 'Virginia Woolf'}) -REMOVE author.name ----- - -In this case, the property is not removed because it is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Node(0) with label `Author` must have the property `name` ----- - -====== - - -[[constraints-fail-to-create-a-property-existence-constraint-due-to-existing-node]] -=== Creating a constraint when there exist conflicting nodes will fail - - -.+CREATE CONSTRAINT+ -====== - -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, role=test-fail] ----- -CREATE CONSTRAINT author_nationality FOR (author:Author) REQUIRE author.nationality IS NOT NULL ----- - -In this case, the constraint cannot be created because it is in conflict with the existing graph. -Remove or correct the offending nodes and then re-apply the constraint. - -.Error message -[source, error] ----- -Unable to create Constraint( type='NODE PROPERTY EXISTENCE', schema=(:Author {nationality}) ): -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 graph. -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=label--enterprise-edition] -[[constraints-examples-relationship-property-existence]] -== Relationship property existence constraints - -A relationship property existence constraint ensures that certain relationships 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-by-param[] -* 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[] - - -[[constraints-create-a-relationship-property-existence-constraint]] -=== Create a relationship property existence constraint - -When creating a relationship property existence constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT wrote_year -FOR ()-[wrote:WROTE]-() REQUIRE wrote.year IS NOT NULL ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view for property existence constraints, `Property existence constraints added: 1`, will be split between nodes and relationships in Neo4j 6.0. -For the relationship property existence constraints, they will say `Relationship property existence constraints added: 1`. -==== - -====== - -[role=label--new-5.16] -[[constraints-create-a-relationship-property-existence-constraint-by-param]] -=== Create a relationship property existence constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "rel_exist_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR ()-[wrote:WROTE]-() REQUIRE wrote.published IS NOT NULL ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -[NOTE] -==== -The detailed statistics view for property existence constraints, `Property existence constraints added: 1`, will be split between nodes and relationships in Neo4j 6.0. -For the relationship property existence constraints, they will say `Relationship property existence constraints added: 1`. -==== - -====== - - -[[constraints-create-a-relationship-property-existence-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 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. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT wrote_year IF NOT EXISTS -FOR ()-[wrote:WROTE]-() REQUIRE wrote.year IS NOT NULL ----- - -Assuming that such a constraint already exists: - -.Result -[queryresult] ----- -(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. ----- - -====== - - -[[constraints-create-an-already-existing-relationship-property-existence-constraint]] -=== Creating an already existing constraint will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a named relationship property existence constraint on the property `locations` on relationships with the `WROTE` relationship type, when a constraint with the given name already exists: - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT wrote_locations FOR ()-[wrote:WROTE]-() REQUIRE wrote.location IS NOT NULL ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT wrote_locations -FOR ()-[wrote:WROTE]-() REQUIRE wrote.locations IS NOT NULL ----- - -In this case, the constraint cannot be created because there already exists a constraint with the given name. - -.Error message -[source, error] ----- -There already exists a constraint called 'wrote_locations'. ----- - -====== - - -[[constraints-create-a-relationship-that-complies-with-a-property-existence-constraint]] -=== Creating a relationship that complies with an existing constraint - - -.+CREATE RELATIONSHIP+ -====== - -Create a `WROTE` relationship with a `year` and `location` property, given property existence constraints on `:WROTE(year)` and `:WROTE(location)`: - -.Query -[source, cypher] ----- -CREATE (author:Author {name: 'Emily Brontë', surname: 'Brontë'})-[wrote:WROTE {year: 1847, location: 'Haworth, United Kingdom', published: true}]->(book:Book {title:'Wuthering Heights', isbn: 9789186579296}) ----- - -.Result -[queryresult] ----- -Added 2 labels, created 2 nodes, set 7 properties, created 1 relationship ----- - -====== - - -[[constraints-create-a-relationship-that-violates-a-property-existence-constraint]] -=== Creating a relationship that violates an existing constraint will fail - - -.+CREATE RELATIONSHIP+ -====== - -Create a `WROTE` relationship without a `location` property, given a property existence constraint `:WROTE(location)`: - -.Query -[source, cypher, role=test-fail] ----- -CREATE (author:Author {name: 'Charlotte Brontë', surname: 'Brontë'})-[wrote:WROTE {year: 1847, published: true}]->(book:Book {title: 'Jane Eyre', isbn:9780194241762}) ----- - -In this case, the relationship is not created because it is missing the `location` property which is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Relationship(0) with type `WROTE` must have the property `location` ----- - -====== - - -[[constraints-removing-an-existence-constrained-relationship-property]] -=== Removing an existence constrained relationship property will fail - - -.+REMOVE PROPERTY+ -====== - -Remove the `location` property from an existing relationship of relationship type `WROTE`, given a property existence constraint `:WROTE(location)`: - -.Query -[source, cypher, role=test-fail] ----- -MATCH (author:Author)-[wrote:WROTE]->(book:Book) REMOVE wrote.location ----- - -In this case, the property is not removed because it is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Relationship(0) with type `WROTE` must have the property `location` ----- - -====== - - -[[constraints-fail-to-create-a-property-existence-constraint-due-to-existing-relationship]] -=== Creating a constraint when there exist conflicting relationships will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a constraint on the property `language` on relationships with the `WROTE` relationship type, when there already exists a relationship without a property named `language`: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT wrote_language FOR ()-[wrote:WROTE]-() REQUIRE wrote.language IS NOT NULL ----- - -In this case, the constraint cannot be created because it is in conflict with the existing graph. -Remove or correct the offending relationships and then re-apply the constraint. - -.Error message -[source, error] ----- -Unable to create Constraint( type='RELATIONSHIP PROPERTY EXISTENCE', schema=()-[:WROTE {language}]-() ): -Relationship(0) with type `WROTE` must have the property `language` ----- - -====== - -The constraint creation fails on the first offending relationship that is found. -This does not guarantee that there are no other offending relationships in the graph. -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=label--enterprise-edition label--new-5.9] -[[constraints-examples-node-property-type]] -== Node property type constraints - -A node property type constraint ensures that certain nodes have a property of the required property type when the property exists on the node. - -The allowed property types for the constraints are: - -* `BOOLEAN` -* `STRING` -* `INTEGER` -* `FLOAT` -* `DATE` -* `LOCAL TIME` -* `ZONED TIME` -* `LOCAL DATETIME` -* `ZONED DATETIME` -* `DURATION` -* `POINT` -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* Any closed dynamic union of the above types, e.g. `INTEGER | FLOAT | STRING`. label:new[Introduced in 5.11] - -For a complete reference describing all types available in Cypher, see the section on xref::values-and-types/property-structural-constructed.adoc#types-synonyms[types and their synonyms]. - -* xref::constraints/examples.adoc#constraints-create-a-node-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-node-property-type-constraint-by-param[] -* xref::constraints/examples.adoc#constraints-create-a-node-property-type-constraint-if-not-exist[] -* xref::constraints/examples.adoc#constraints-create-an-already-existing-node-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-different-than-existing-node-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-node-property-type-constraints-on-invalid-types[] -* xref::constraints/examples.adoc#constraints-create-a-node-that-complies-with-a-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-node-that-violates-a-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-fail-to-create-a-property-type-constraint-due-to-existing-node[] - - -[[constraints-create-a-node-property-type-constraint]] -=== Create a node property type constraint - -When creating a node property type constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT movie_title -FOR (movie:Movie) REQUIRE movie.title IS :: STRING ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -[role=label--new-5.16] -[[constraints-create-a-node-property-type-constraint-by-param]] -=== Create a node property type constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "node_prop_type_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR (movie:Movie) REQUIRE movie.prop1 IS :: INT ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - - -[role=label--new-5.11] -[[constraints-create-a-node-property-type-constraint-union]] -=== Create a node property type constraint with a union type - -A closed dynamic union allows a node property to maintain some type flexibility whilst preventing unexpected values from being stored. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT movie_tagline -FOR (movie:Movie) REQUIRE movie.tagline IS :: STRING | LIST ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - - -[[constraints-create-a-node-property-type-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 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. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -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: - -.Result -[queryresult] ----- -(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. ----- - -====== - - -[[constraints-create-an-already-existing-node-property-type-constraint]] -=== Creating an already existing constraint will fail - - -.+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: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT movies -FOR (movie:Movie) REQUIRE movie.title IS TYPED STRING ----- - -In this case, the constraint cannot be created because it already exists. - -.Error message -[source, error] ----- -Constraint already exists: Constraint( id=22, name='movie_title', type='NODE PROPERTY TYPE', schema=(:Movie {title}), propertyType=STRING ) ----- - -====== - - -[[constraints-create-a-different-than-existing-node-property-type-constraint]] -=== Creating a constraint when there is an existing constraint with a different property type will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a node property type constraint restricting the property `seriesOrder` to float values on nodes with the `Movie` label, when a node property type constraint restricting the property `seriesOrder` to integer values already exists: - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT seriesOrder -FOR (movie:Movie) REQUIRE movie.seriesOrder IS :: INTEGER ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT order -FOR (movie:Movie) REQUIRE movie.seriesOrder IS :: FLOAT ----- - -In this case, the constraint cannot be created because there exists a conflicting constraint. - -.Error message -[source, error] ----- -Constraint already exists: Constraint( id=23, name='seriesOrder', type='NODE PROPERTY TYPE', schema=(:Movie {seriesOrder}), propertyType=INTEGER ) ----- - -====== - - -[[constraints-create-node-property-type-constraints-on-invalid-types]] -=== Creating constraints on invalid types will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a node property type constraint restricting the property `imdbScore` to map values on nodes with the `Movie` label: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT score -FOR (movie:Movie) REQUIRE movie.imdbScore IS :: MAP ----- - -In this case, the constraint cannot be created because values of type `MAP` cannot be stored in properties and therefore are not permitted in property type constraints. - -.Error message -[source, error] ----- -Failed to create node property type constraint: Invalid property type `MAP`. ----- - -====== - -.+CREATE CONSTRAINT+ -====== - -Create a node property type constraint restricting the property `imdbScore` to list of nullable float values on nodes with the `Movie` label: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT score -FOR (movie:Movie) REQUIRE movie.imdbScore IS :: LIST ----- - -In this case, the constraint cannot be created because the inner type of list types cannot be nullable. -The correct type to use for the constraint is `LIST` because `null` values cannot be stored as part of a list. - -.Error message -[source, error] ----- -Failed to create node property type constraint: Invalid property type `LIST`. Lists cannot have nullable inner types. ----- - -====== - -.+CREATE CONSTRAINT+ -====== - -Create a node property type constraint restricting the property `imdbScore` to list of lists of float values on nodes with the `Movie` label: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT score -FOR (movie:Movie) REQUIRE movie.imdbScore IS :: LIST> ----- - -In this case, the constraint cannot be created because the inner type of list types cannot be other lists. - -.Error message -[source, error] ----- -Failed to create node property type constraint: Invalid property type `LIST>`. Lists cannot have lists as an inner type. ----- - -====== - - -[[constraints-create-a-node-that-complies-with-a-property-type-constraint]] -=== Creating a node that complies with an existing constraint - - -.+CREATE NODE+ -====== - -Create an `Movie` node with a `STRING` `title` property: - -.Query -[source, cypher] ----- -CREATE (movie:Movie {title:'Iron Man'}) ----- - -.Result -[queryresult] ----- -Added 1 label, created 1 node, set 1 properties ----- - -====== - - -[[constraints-create-a-node-that-violates-a-property-type-constraint]] -=== Creating a node that violates an existing constraint will fail - - -.+CREATE NODE+ -====== - -Create a `Movie` node with an integer `title` property, given a property type constraint on the label `Movie` restricting the `title` property to `STRING` values: - -.Query -[source, cypher, role=test-fail] ----- -CREATE (movie:Movie {title: 123}) ----- - -In this case, the node is not created because the `title` property is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Node(0) with label `Movie` has property `title` of wrong type `Long`. Allowed types: STRING ----- - -====== - - -[[constraints-fail-to-create-a-property-type-constraint-due-to-existing-node]] -=== Creating a constraint when there exist conflicting nodes will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a constraint restricting the property `franchise` to `STRING` values on nodes with the `Movie` label, when there already exists a node with a `BOOLEAN` `franchise` property: - -//// -[source, cypher, role=test-setup] ----- -CREATE (movie:Movie {title:'Captain America: The First Avenger', franchise: true}) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT movie_franchise FOR (movie:Movie) REQUIRE movie.franchise IS :: STRING ----- - -In this case, the constraint cannot be created because it is in conflict with the existing graph. -Remove or correct the offending nodes and then re-apply the constraint. - -.Error message -[source, error] ----- -Unable to create Constraint( name='movie_franchise', type='NODE PROPERTY TYPE', schema=(:Movie {franchise}), propertyType=STRING ): -Node(0) with label `Movie` has property `franchise` of wrong type `Boolean`. Allowed types: STRING ----- - -====== - -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 graph. -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 wrong property type for the constraint above: - -.Query -[source, cypher] ----- -MATCH (movie:Movie) -WHERE movie.franchise IS NOT :: STRING -RETURN movie ----- - - -[role=label--enterprise-edition label--new-5.9] -[[constraints-examples-relationship-property-type]] -== Relationship property type constraints - -A relationship property type constraint ensures that certain relationships have a property of the required property type when the property exists on the relationship. - -The allowed property types for the constraints is: - -* `BOOLEAN` -* `STRING` -* `INTEGER` -* `FLOAT` -* `DATE` -* `LOCAL TIME` -* `ZONED TIME` -* `LOCAL DATETIME` -* `ZONED DATETIME` -* `DURATION` -* `POINT` -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* Any closed dynamic union of the above types, e.g. `INTEGER | FLOAT | STRING`. label:new[Introduced in 5.11] - -For a complete reference describing all types available in Cypher, see the section on xref::values-and-types/property-structural-constructed.adoc#types-synonyms[types and their synonyms]. - -* xref::constraints/examples.adoc#constraints-create-a-relationship-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-relationship-property-type-constraint-by-param[] -* xref::constraints/examples.adoc#constraints-create-a-relationship-property-type-constraint-if-not-exist[] -* xref::constraints/examples.adoc#constraints-create-an-already-existing-relationship-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-different-than-existing-relationship-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-relationship-property-type-constraints-on-invalid-types[] -* xref::constraints/examples.adoc#constraints-create-a-relationship-that-complies-with-a-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-create-a-relationship-that-violates-a-property-type-constraint[] -* xref::constraints/examples.adoc#constraints-fail-to-create-a-property-type-constraint-due-to-existing-relationship[] - - -[[constraints-create-a-relationship-property-type-constraint]] -=== Create a relationship property type constraint - -When creating a relationship property type constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT part_of -FOR ()-[part:PART_OF]-() REQUIRE part.order IS :: INTEGER ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -[role=label--new-5.16] -[[constraints-create-a-relationship-property-type-constraint-by-param]] -=== Create a relationship property type constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "rel_prop_type_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR ()-[part:PART_OF]-() REQUIRE part.prop1 IS :: FLOAT ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -[role=label--new-5.11] -[[constraints-create-a-relationship-property-type-constraint-union]] -=== Create a relationship property type constraint with a union type - -A closed dynamic union allows a relationship property to maintain some type flexibility whilst preventing unexpected values from being stored. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT part_of_tags -FOR ()-[part:PART_OF]-() REQUIRE part.tags IS :: STRING | LIST ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -[[constraints-create-a-relationship-property-type-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 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. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT part_of IF NOT EXISTS -FOR ()-[part:PART_OF]-() REQUIRE part.order IS TYPED INTEGER ----- - -Assuming that such a constraint already exists: - -.Result -[queryresult] ----- -(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. ----- - -====== - - -[[constraints-create-an-already-existing-relationship-property-type-constraint]] -=== Creating an already existing constraint will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a relationship property type constraint restricting the property `order` to integer values on relationships with the `PART_OF` relationship type, when that constraint already exists: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT belongs_to -FOR ()-[part:PART_OF]-() REQUIRE part.order :: INTEGER ----- - -In this case, the constraint cannot be created because it already exists (but with a different name). - -.Error message -[source, error] ----- -Constraint already exists: Constraint( id=24, name='part_of', type='RELATIONSHIP PROPERTY TYPE', schema=()-[:PART_OF {order}]-(), propertyType=INTEGER ) ----- - -====== - - -[[constraints-create-a-different-than-existing-relationship-property-type-constraint]] -=== Creating a constraint when there is an existing constraint with a different property type will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a relationship property type constraint restricting the property `order` to float values on relationships with the `PART_OF` relationship type, when a relationship property type constraint restricting the property `order` to integer values already exists: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT order -FOR ()-[part:PART_OF]-() REQUIRE part.order IS :: FLOAT ----- - -In this case, the constraint cannot be created because there exists a conflicting constraint. - -.Error message -[source, error] ----- -Constraint already exists: Constraint( id=24, name='part_of', type='RELATIONSHIP PROPERTY TYPE', schema=()-[:PART_OF {order}]-(), propertyType=INTEGER ) ----- - -====== - - -[[constraints-create-relationship-property-type-constraints-on-invalid-types]] -=== Creating constraints on invalid types will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a relationship property type constraint restricting the property `releaseOrder` to integer values excluding `null` on relationships with the `PART_OF` relationship type: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT score -FOR ()-[part:PART_OF]-() REQUIRE part.releaseOrder IS :: INTEGER NOT NULL ----- - -In this case, the constraint cannot be created because excluding `null` is not allowed in property type constraints. -To also ensure that the property exists (is not `null`), add an existence constraint on the property. - -.Error message -[source, error] ----- -Failed to create relationship property type constraint: Invalid property type `INTEGER NOT NULL`. ----- - -====== - -.+CREATE CONSTRAINT+ -====== - -Create a relationship property type constraint restricting the property `releaseOrder` to list of nullable integer values on relationships with the `PART_OF` relationship type: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT score -FOR ()-[part:PART_OF]-() REQUIRE part.releaseOrder IS :: LIST ----- - -In this case, the constraint cannot be created because the inner type of list types cannot be nullable. -The correct type to use for the constraint is `LIST` because `null` values cannot be stored as part of a list. - -.Error message -[source, error] ----- -Failed to create relationship property type constraint: Invalid property type `LIST`. Lists cannot have nullable inner types. ----- - -====== - -.+CREATE CONSTRAINT+ -====== - -Create a relationship property type constraint restricting the property `releaseOrder` to list of lists of integer values on relationships with the `PART_OF` relationship type: - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT score -FOR ()-[part:PART_OF]-() REQUIRE part.releaseOrder IS :: LIST> ----- - -In this case, the constraint cannot be created because the inner type of list types cannot be other lists. - -.Error message -[source, error] ----- -Failed to create relationship property type constraint: Invalid property type `LIST>`. Lists cannot have lists as an inner type. ----- - -====== - - -[[constraints-create-a-relationship-that-complies-with-a-property-type-constraint]] -=== Creating a relationship that complies with an existing constraint - - -.+CREATE RELATIONSHIP+ -====== - -Create a `PART_OF` relationship with an integer `order` property: - -//// -[source, cypher, role=test-setup] ----- -CREATE (franchise:Franchise {name:'MCU'}) ----- -//// - -.Query -[source, cypher] ----- -MATCH (movie:Movie {title:'Iron Man'}), (franchise:Franchise {name:'MCU'}) -CREATE (movie)-[part:PART_OF {order: 3}]->(franchise) ----- - -.Result -[queryresult] ----- -Set 1 property, created 1 relationship ----- - -====== - - -[[constraints-create-a-relationship-that-violates-a-property-type-constraint]] -=== Creating a relationship that violates an existing constraint will fail - - -.+CREATE RELATIONSHIP+ -====== - -Create a `PART_OF` relationship with a `STRING` `order` property, given a property type constraint on the relationship type `PART_OF` restricting the `order` property to integer values: - -.Query -[source, cypher, role=test-fail] ----- -MATCH (movie:Movie {title:'Captain America: The First Avenger'}), (franchise:Franchise {name:'MCU'}) -CREATE (movie)-[part:PART_OF {order: '1'}]->(franchise) ----- - -In this case, the relationship is not created because the `order` property is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Relationship(0) with type `PART_OF` has property `order` of wrong type `String`. Allowed types: INTEGER ----- - -====== - - -[[constraints-fail-to-create-a-property-type-constraint-due-to-existing-relationship]] -=== Creating a constraint when there exist conflicting relationships will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a constraint restricting the property `releaseOrder` to integer values on relationships with the `PART_OF` relationship type, when there already exists a relationship with a `STRING` `releaseOrder` property: - -//// -[source, cypher, role=test-setup] ----- -MATCH (movie:Movie {title:'Captain America: The First Avenger'}), (franchise:Franchise {name:'MCU'}) -CREATE (movie)-[part:PART_OF {order: 1, releaseOrder: '5'}]->(franchise) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT release_order -FOR ()-[part:PART_OF]-() REQUIRE part.releaseOrder IS :: INTEGER ----- - -In this case, the constraint cannot be created because it is in conflict with the existing graph. -Remove or correct the offending relationships and then re-apply the constraint. - -.Error message -[source, error] ----- -Unable to create Constraint( name='release_order', type='RELATIONSHIP PROPERTY TYPE', schema=()-[:PART_OF {releaseOrder}]-(), propertyType=INTEGER ): -Relationship(0) with type `PART_OF` has property `releaseOrder` of wrong type `String`. Allowed types: INTEGER ----- - -====== - -The constraint creation fails on the first offending relationship that is found. -This does not guarantee that there are no other offending relationships in the graph. -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 with the wrong property type for the constraint above: - -.Query -[source, cypher] ----- -MATCH ()-[part:PART_OF]-() -WHERE part.releaseOrder IS NOT :: INTEGER -RETURN part ----- - - -[role=label--enterprise-edition] -[[constraints-examples-node-key]] -== Node key constraints - -A node key constraint ensures that certain nodes have a set of specified 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-by-param[] -* 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[] - - -[[constraints-create-a-node-key-constraint]] -=== Create a node key constraint - -When creating a node key constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT actor_fullname -FOR (actor:Actor) REQUIRE (actor.firstname, actor.surname) IS NODE KEY ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -[role=label--new-5.16] -[[constraints-create-a-node-key-constraint-by-param]] -=== Create a node key constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "node_key_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR (actor:Actor) REQUIRE actor.firstname IS KEY ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - - -[[constraints-create-a-node-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 that no constraint is created if any other constraint with the given name, or another node key constraint on the same schema, already exists. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT actor_names IF NOT EXISTS -FOR (actor:Actor) REQUIRE (actor.firstname, actor.surname) IS NODE KEY ----- - -Assuming a node key constraint on `(:Actor {firstname, surname})` already exists: - -.Result -[queryresult] ----- -(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. ----- - -====== - - -[[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. - -The only valid value for the index provider is: - -* `range-1.0` label:default[] - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT constraint_with_provider -FOR (actor:Actor) REQUIRE (actor.surname) IS NODE KEY -OPTIONS { - indexProvider: 'range-1.0' -} ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -There is no valid index configuration values for the constraint-backing range indexes. - - -[[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 `Actor` label, when a property uniqueness constraint already exists on the same label and property combination: - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT preExisting_actor_name_age FOR (actor:Actor) REQUIRE (actor.firstname, actor.age) IS UNIQUE ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT actor_name_age FOR (actor:Actor) REQUIRE (actor.firstname, actor.age) IS NODE KEY ----- - -In this case, the constraint cannot be created because there already exist a conflicting constraint on that label and property combination. - -.Error message -[source, error] ----- -Constraint already exists: -Constraint( id=10, name='preExisting_actor_name_age', type='UNIQUENESS', schema=(:Actor {firstname, age}), ownedIndex=9 ) ----- - -====== - - -[[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+ -====== - -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: - -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX citizenship FOR (person:Person) ON (person.citizenship) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -CREATE CONSTRAINT citizenship -FOR (actor:Actor) REQUIRE actor.citizenship IS NODE KEY ----- - -In this case, the constraint cannot be created because there already exists an index with the given name. - -.Error message -[source, error] ----- -There already exists an index called 'citizenship'. ----- - -====== - - -[[constraints-create-a-node-that-complies-with-a-node-key-constraint]] -=== Creating a node that complies with an existing constraint - - -.+CREATE NODE+ -====== - -Create an `Actor` node with `firstname` and `surname` properties: - -.Query -[source, cypher] ----- -CREATE (actor:Actor {firstname: 'Keanu', surname: 'Reeves'}) ----- - -.Result -[queryresult] ----- -Added 1 label, created 1 node, set 2 properties. ----- - -====== - - -[[constraints-create-a-node-that-violates-a-node-key-constraint]] -=== Creating a node that violates an existing constraint will fail - - -.+CREATE NODE+ -====== - -Create an `Actor` node without a `firstname` property, given a node key constraint on `:Actor(firstname, surname)`: - - -.Query -[source, cypher, role=test-fail] ----- -CREATE (actor:Actor {surname: 'Wood'}) ----- - -In this case, the node is not created because it is missing the `firstname` property which is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Node(0) with label `Actor` must have the properties (`firstname`, `surname`) ----- - -====== - - -[[constraints-removing-a-node-key-constrained-property]] -=== Removing a +NODE KEY+-constrained property will fail - - -.+REMOVE PROPERTY+ -====== - -Remove the `firstname` property from an existing node `Actor`, given a `NODE KEY` constraint on `:Actor(firstname, surname)`: - -.Query -[source, cypher, role=test-fail] ----- -MATCH (actor:Actor {firstname: 'Keanu', surname: 'Reeves'}) REMOVE actor.firstname ----- - -In this case, the property is not removed because it is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Node(0) with label `Actor` must have the properties (`firstname`, `surname`) ----- - -====== - - -[[constraints-fail-to-create-a-node-key-constraint-due-to-existing-node]] -=== Creating a constraint when there exist conflicting node will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a node key constraint on the property `born` on nodes with the `Actor` label, when a node without a `born` property already exists in the graph: - -.Query -[source, cypher, role=test-fail] ----- -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/search-performance-indexes/managing-indexes.adoc[] instead, or remove/correct the offending nodes and then re-apply the constraint. - -.Error message -[source, error] ----- -Unable to create Constraint( type='NODE KEY', schema=(:Actor {born}) ): -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 graph. -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 ----- - - -[role=label--enterprise-edition label--new-5.7] -[[constraints-examples-relationship-key]] -== Relationship key constraints - -A relationship key constraint ensures that certain relationships 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-by-param[] -* 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[] - - -[[constraints-create-a-relationship-key-constraint]] -=== Create a relationship key constraint - -When creating a relationship key constraint, it is recommended to provide a constraint name. - - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT knows_since_how -FOR ()-[knows:KNOWS]-() REQUIRE (knows.since, knows.how) IS RELATIONSHIP KEY ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -[role=label--new-5.16] -[[constraints-create-a-relationship-key-constraint-by-param]] -=== Create a relationship key constraint using a parameter - -The constraint name can also be given as a parameter. - -.+CREATE CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "rel_key_param" -} ----- - -.Query -[source, cypher] ----- -CREATE CONSTRAINT $name -FOR ()-[knows:KNOWS]-() REQUIRE knows.friend IS KEY ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - - -[[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 that no constraint is created if any other constraint with the given name, or another relationship key constraint on the same schema, already exists. -As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. - -.+CREATE CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -CREATE CONSTRAINT knows IF NOT EXISTS -FOR ()-[knows:KNOWS]-() REQUIRE (knows.since, knows.how) IS RELATIONSHIP KEY ----- - -Assuming a relationship key constraint on `()-[:KNOWS {since, how}]-()` already exists: - -.Result -[queryresult] ----- -(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. ----- - -====== - - -[[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] ----- -CREATE CONSTRAINT rel_constraint_with_provider -FOR ()-[knows:KNOWS]-() REQUIRE (knows.since) IS REL KEY -OPTIONS { - indexProvider: 'range-1.0' -} ----- - -.Result -[queryresult] ----- -Added 1 constraint. ----- - -====== - -There is no valid index configuration values for the constraint-backing range indexes. - - -[[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 property `how` on relationships with the `KNOWS` relationship type, when a property uniqueness constraint already exists on the same relationship type and property combination: - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT preExisting_how FOR ()-[knows:KNOWS]-() REQUIRE (knows.how) IS UNIQUE ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -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. - -.Error message -[source, error] ----- -Constraint already exists: -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 6.0. -==== - -====== - - -[[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 `level` on relationships with the `KNOWS` relationship type, when an index already exists with the given name: - -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX knows FOR ()-[know:KNOW]-() ON (know.levels) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -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. - -.Error message -[source, error] ----- -There already exists an index called 'knows'. ----- - -====== - - -[[constraints-create-a-relationship-that-complies-with-a-relationship-key-constraint]] -=== Creating a relationship that complies with an existing constraint - - -.+CREATE RELATIONSHIP+ -====== - -Create a `KNOWS` relationship with both `since` and `how` properties and a relationship key constraint on `:KNOWS(since, how)`: - -.Query -[source, cypher] ----- -CREATE (:Actor {firstname: 'Jensen', surname: 'Ackles'})-[:KNOWS {since: 2008, how: 'coworkers', friend: true}]->(:Actor {firstname: 'Misha', surname: 'Collins'}) ----- - -.Result -[queryresult] ----- -Added 2 labels, created 2 nodes, set 6 properties, created 1 relationship. ----- - -====== - - -[[constraints-create-a-relationship-that-violates-a-relationship-key-constraint]] -=== Creating a relationship that violates an existing constraint will fail - - -.+CREATE RELATIONSHIP+ -====== - -Create a `KNOWS` relationship without a `since` property, given a relationship key constraint on `:KNOWS(since, how)`: - -.Query -[source, cypher, role=test-fail] ----- -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 because it is missing the `since` property which is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Relationship(0) already exists with type `KNOWS` and property `how` = 'coworkers' ----- - -====== - - -[[constraints-removing-a-relationship-key-constrained-property]] -=== Removing a +RELATIONSHIP KEY+-constrained property will fail - - -.+REMOVE PROPERTY+ -====== - -Remove the `since` property from an existing relationship `KNOWS`, given a `RELATIONSHIP KEY` constraint on `:KNOWS(since, how)`: - -.Query -[source, cypher, role=test-fail] ----- -MATCH ()-[knows:KNOWS {since: 2008, how: 'coworkers'}]->() REMOVE knows.since ----- - -In this case, the property is not removed because it is in conflict with an existing constraint. - -.Error message -[source, error] ----- -Relationship(0) with type `KNOWS` must have the properties (`since`, `how`) ----- - -====== - - -[[constraints-fail-to-create-a-relationship-key-constraint-due-to-existing-relationship]] -=== Creating a constraint when there exist conflicting relationships will fail - - -.+CREATE CONSTRAINT+ -====== - -Create a relationship key constraint on the property `level` on relationships with the `KNOWS` relationship type, when two relationships with identical `level` property values already exist in the graph: - -//// -[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', friend: false, level: 10}]->(:Actor {firstname: 'Jared', surname: 'Padalecki'}) ----- -//// - -.Query -[source, cypher, role=test-fail] ----- -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 in conflict with the existing graph. -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] ----- -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 graph. -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 - -A constraint can be dropped using the name with the `DROP CONSTRAINT constraint_name` command. -It is the same command for all constraint types. -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`. - -* xref::constraints/examples.adoc#constraints-drop-a-constraint[] -* xref::constraints/examples.adoc#constraints-drop-a-constraint-by-param[] -* xref::constraints/examples.adoc#constraints-drop-a-non-existing-constraint[] - - -[[constraints-drop-a-constraint]] -=== Drop a constraint - -A constraint can be dropped using the name with the `DROP CONSTRAINT` command. - - -.+DROP CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -DROP CONSTRAINT book_isbn ----- - -.Result -[queryresult] ----- -Removed 1 constraint. ----- - -====== - - -[role=label--new-5.16] -[[constraints-drop-a-constraint-by-param]] -=== Drop a constraint using a parameter - -The constraint name can also be given as a parameter. - -.+DROP CONSTRAINT+ -====== - -.Parameters -[source, parameters] ----- -{ - "name": "node_uniqueness_param" -} ----- - -.Query -[source, cypher] ----- -DROP CONSTRAINT $name ----- - -.Result -[queryresult] ----- -Removed 1 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`. - -.+DROP CONSTRAINT+ -====== - -.Query -[source, cypher] ----- -DROP CONSTRAINT missing_constraint_name IF EXISTS ----- - -.Result -[queryresult] ----- -(no changes, no records) ----- - -.Notification -[source] ----- -`DROP CONSTRAINT missing_constraint_name IF EXISTS` has no effect. `missing_constraint_name` does not exist. ----- - -====== - - -[[constraints-examples-list-constraint]] -== Listing constraints - -* xref::constraints/examples.adoc#constraints-listing-all-constraints[] -* xref::constraints/examples.adoc#constraints-listing-constraints-with-filtering[] - - -[[constraints-listing-all-constraints]] -=== Listing all constraints - -To list all constraints with the default output columns, the `SHOW CONSTRAINTS` command can be used. -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#constraints-syntax-drop[`DROP CONSTRAINT` command]. -==== - - -.+SHOW CONSTRAINTS+ -====== - -.Query -[source, cypher, test-exclude-cols=id] ----- -SHOW CONSTRAINTS ----- - -[queryresult] ----- -+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | propertyType | -+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 36 | "actor_fullname" | "NODE_KEY" | "NODE" | ["Actor"] | ["firstname", "surname"] | "actor_fullname" | null | -| 21 | "author_name" | "NODE_PROPERTY_EXISTENCE" | "NODE" | ["Author"] | ["name"] | null | null | -| 24 | "author_pseudonym" | "UNIQUENESS" | "NODE" | ["Author"] | ["pseudonym"] | "author_pseudonym" | null | -| 8 | "book_isbn2" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn2"] | "book_isbn2" | null | -| 10 | "constraint_with_options" | "UNIQUENESS" | "NODE" | ["Book"] | ["prop1", "prop2"] | "constraint_with_options" | null | -| 40 | "constraint_with_provider" | "NODE_KEY" | "NODE" | ["Actor"] | ["surname"] | "constraint_with_provider" | null | -| 45 | "knows_since_how" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["since", "how"] | "knows_since_how" | null | -| 30 | "movie_tagline" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["tagline"] | null | "STRING | LIST" | -| 28 | "movie_title" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["title"] | null | "STRING" | -| 22 | "node_exist_param" | "NODE_PROPERTY_EXISTENCE" | "NODE" | ["Author"] | ["surname"] | null | null | -| 38 | "node_key_param" | "NODE_KEY" | "NODE" | ["Actor"] | ["firstname"] | "node_key_param" | null | -| 29 | "node_prop_type_param" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["prop1"] | null | "INTEGER" | -| 32 | "part_of" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["order"] | null | "INTEGER" | -| 34 | "part_of_tags" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["tags"] | null | "STRING | LIST" | -| 42 | "preExisting_actor_name_age" | "UNIQUENESS" | "NODE" | ["Actor"] | ["firstname", "age"] | "preExisting_actor_name_age" | null | -| 12 | "preExisting_book_published" | "UNIQUENESS" | "NODE" | ["Book"] | ["published"] | "preExisting_book_published" | null | -| 51 | "preExisting_how" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["KNOWS"] | ["how"] | "preExisting_how" | null | -| 19 | "rel_constraint_with_options" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["order", "seriesTitle", "number"] | "rel_constraint_with_options" | null | -| 49 | "rel_constraint_with_provider" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "rel_constraint_with_provider" | null | -| 26 | "rel_exist_param" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["published"] | null | null | -| 47 | "rel_key_param" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["friend"] | "rel_key_param" | null | -| 33 | "rel_prop_type_param" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["prop1"] | null | "FLOAT" | -| 17 | "rel_uniqueness_param" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["prop1"] | "rel_uniqueness_param" | null | -| 15 | "sequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["order", "seriesTitle"] | "sequels" | null | -| 31 | "seriesOrder" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["seriesOrder"] | null | "INTEGER" | -| 27 | "wrote_locations" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["location"] | null | null | -| 25 | "wrote_year" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["year"] | null | null | -+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -27 rows ----- - -[NOTE] -==== -The `type` column returns `UNIQUENESS` for the node property uniqueness constraint and `RELATIONSHIP_UNIQUENESS` for the relationship property uniqueness constraint. -This will be updated in Neo4j 6.0. -Node property uniqueness constraints will be updated to `NODE_PROPERTY_UNIQUENESS` and relationship property uniqueness constraints to `RELATIONSHIP_PROPERTY_UNIQUENESS`. -==== - -====== - - -[[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 constraint type keywords, -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. - - -.+SHOW CONSTRAINTS+ -====== - -.Query -[source, cypher, test-exclude-cols=id] ----- -SHOW EXISTENCE CONSTRAINTS -WHERE entityType = 'RELATIONSHIP' ----- - -This will only return the default output columns. -To get all columns, use `+SHOW INDEXES YIELD * WHERE ...+`. - -[queryresult] ----- -+-----------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | propertyType | -+-----------------------------------------------------------------------------------------------------------------------------------------+ -| 26 | "rel_exist_param" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["published"] | null | null | -| 27 | "wrote_locations" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["location"] | null | null | -| 25 | "wrote_year" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["year"] | null | null | -+-----------------------------------------------------------------------------------------------------------------------------------------+ -3 rows ----- - -====== - diff --git a/modules/ROOT/pages/constraints/index.adoc b/modules/ROOT/pages/constraints/index.adoc index 2d5677e04..7e5341bba 100644 --- a/modules/ROOT/pages/constraints/index.adoc +++ b/modules/ROOT/pages/constraints/index.adoc @@ -1,124 +1,16 @@ -:description: This section explains how to manage constraints used for ensuring data integrity. +:description: Overview of Neo4j's constraints used for ensuring data integrity. include::https://raw.githubusercontent.com/neo4j-graphacademy/courses/main/asciidoc/courses/cypher-indexes-constraints/ad.adoc[] - -[[constraints]] = Constraints -This page contains an overview of the available constraints in Cypher, and information about how constraints can impact indexes. - -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. - -[[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). - -For more information, see xref:constraints/examples.adoc#constraints-examples-node-uniqueness[examples of node property uniqueness constraints]. - -[role=label--new-5.7] -[[unique-relationship-property]] -== 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 on which the constraint exists are not subject to this rule). - -For more information, see xref:constraints/examples.adoc#constraints-examples-relationship-uniqueness[examples of relationship property uniqueness constraints]. - -[[node-property-existence]] -[role=label--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. - -For more information, see xref:constraints/examples.adoc#constraints-examples-node-property-existence[examples of node property existence constraints]. - -[[relationship-property-existence]] -[role=label--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. - -For more information, see xref:constraints/examples.adoc#constraints-examples-relationship-property-existence[examples of relationship property existence constraints]. - -[[node-property-type]] -[role=label--enterprise-edition label--new-5.9] -== Node property type constraints - -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). - -For more information, see xref:constraints/examples.adoc#constraints-examples-node-property-type[examples of node property type constraints]. - -[[relationship-property-type]] -[role=label--enterprise-edition label--new-5.9] -== Relationship property type constraints - -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). - -For more information, see xref:constraints/examples.adoc#constraints-examples-relationship-property-type[examples of relationship property type constraints]. - -[[node-key]] -[role=label--enterprise-edition] -== Node key constraints - -Node key constraints ensure that, for a given label and set of properties: - -. All the properties exist on all the nodes with that label. -. The combination of the property values is unique. - -+ -Queries attempting to do any of the following will fail: - -* Create new nodes 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. - -For more information, see xref:constraints/examples.adoc#constraints-examples-node-key[examples of node key constraints]. - -[[relationship-key]] -[role=label--enterprise-edition label--new-5.7] -== Relationship key constraints - -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. - -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 - - -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. +Neo4j offers several constraints to ensure the quality and integrity of data in a graph. +The following constraints are available in Neo4j: -[[index-implications]] -== Implications on indexes +* *Property uniqueness constraints* ensure that the combined property values are unique for all nodes with a specific label or all relationships with a specific type. +* *Property existence constraints* ensure that a property exists either for all nodes with a specific label or for all relationships with a specific type. label:enterprise-edition[] +* *Property type constraints* ensure that a property has the required property type for all nodes with a specific label or for all relationships with a specific type. label:new[Introduced in 5.9] label:enterprise-edition[] +* *Key constraints* ensure that all properties exist and that the combined property values are unique for all nodes with a specific label or all relationships with a specific type.label:enterprise-edition[] -Creating a constraint has the following implications on indexes: +To learn more about creating, listing, and dropping these constraints, as well as information about index-backed constraints, constraint creation failures and data violation scenarios, and more, see xref:constraints/managing-constraints.adoc[]. -* 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/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. +For reference material about the Cypher commands used to manage constraints, see xref:constraints/syntax.adoc[]. diff --git a/modules/ROOT/pages/constraints/managing-constraints.adoc b/modules/ROOT/pages/constraints/managing-constraints.adoc new file mode 100644 index 000000000..79f7bbf16 --- /dev/null +++ b/modules/ROOT/pages/constraints/managing-constraints.adoc @@ -0,0 +1,1767 @@ +:description: Information about creating, listing, and dropping Neo4j's constraints. +:page-aliases: constraints/examples.adoc +include::https://raw.githubusercontent.com/neo4j-graphacademy/courses/main/asciidoc/courses/cypher-indexes-constraints/ad.adoc[] += Create, show, and drop constraints + +This page describes how to create, list, and drop constraints. +The following constraint types are available in Neo4j: + +* xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[Property uniqueness constraints] +* xref:constraints/managing-constraints.adoc#create-property-existence-constraints[Property existence constraints] label:enterprise-edition[] +* xref:constraints/managing-constraints.adoc#create-property-type-constraints[Property type constraints] label:new[Introduced in 5.9] label:enterprise-edition[] +* xref:constraints/managing-constraints.adoc#create-key-constraints[Key constraints] label:enterprise-edition[] + + +[[create-constraint]] +== CREATE CONSTRAINT + +Constraints are created with the `CREATE CONSTRAINT` command. +When creating a constraint, it is recommended to provide a constraint 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. + + +[NOTE] +Creating a constraint requires the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-constraints[`CREATE CONSTRAINT` privilege]. + +[NOTE] +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. + +[[create-property-uniqueness-constraints]] +=== Create property uniqueness constraints + +Property uniqueness constraints ensure that the property values are unique for all nodes with a specific label or all relationships with a specific type. +For composite property uniqueness constraints on multiple properties, it is the combination of property values that must be unique. +Queries that try to add duplicated property values will fail. + +Property uniqueness constraints do not require all nodes or relationships to have values for the properties listed in the constraint. +Only nodes or relationships that contain all properties specified in the constraint are subject to the uniqueness rule. +Nodes or relationships missing one or more of the specified properties are not subject to this rule. + +* xref:constraints/managing-constraints.adoc#create-single-property-uniqueness-constraint[] +* xref:constraints/managing-constraints.adoc#create-composite-property-uniqueness-constraint[] +* xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraint-compliant-data[] + +[[create-single-property-uniqueness-constraint]] +==== Create a single property uniqueness constraint + +Single property uniqueness constraints are created with the following commands: + +* Node property uniqueness constraints: `CREATE CONSTRAINT constraint_name FOR (n:Label) REQUIRE n.property IS UNIQUE`. +* Relationship property uniqueness constraints: `CREATE CONSTRAINT constraint_name +FOR ()-[r:REL_TYPE]-() REQUIRE r.property IS UNIQUE`. label:new[Introduced in 5.7] + +For the full command syntax to create a property uniqueness constraint, see xref:constraints/syntax.adoc#create-property-uniqueness-constraints[Syntax -> Create property uniqueness constraints]. + +.Create a node property uniqueness constraint on a single property +====== + +.Create a constraint requiring `Book` nodes to have unique `isbn` properties +[source, cypher] +---- +CREATE CONSTRAINT book_isbn +FOR (book:Book) REQUIRE book.isbn IS UNIQUE +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +[NOTE] +The detailed statistics view currently says `Unique constraints added: 1`. +It will be updated to say `Node property uniqueness constraints added: 1` in a future version of Neo4j. + +====== + + +.Create a relationship property uniqueness constraint on a single property label:new[Introduced in 5.7] +====== + +.Create a constraint requiring `SEQUEL_OF` relationships to have unique `order` properties +[source, cypher] +---- +CREATE CONSTRAINT sequels +FOR ()-[sequel:SEQUEL_OF]-() REQUIRE sequel.order IS UNIQUE +---- + +.Result +[source, queryresult] +---- +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 a future version of Neo4j. + +====== + +[[create-composite-property-uniqueness-constraint]] +==== Create a composite property uniqueness constraint + +Constraints created for multiple properties are called composite constraints. +Note that the constrained properties must be parenthesized when creating composite property uniqueness constraints. + +* Node property uniqueness constraints: `CREATE CONSTRAINT constraint_name FOR (n:Label) REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS UNIQUE`. +* Relationship property uniqueness constraints: `CREATE CONSTRAINT constraint_name FOR ()-[r:REL_TYPE]-() REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS UNIQUE`. label:new[Introduced in 5.7] + +For the full command syntax to create a property uniqueness constraint, see xref:constraints/syntax.adoc#create-property-uniqueness-constraints[Syntax -> Create property uniqueness constraints]. + +.Create a composite node property uniqueness constraint on several properties +====== + +.Create a constraint requiring `Book` nodes to have unique combinations of `title` and `publicationYear` properties +[source, cypher] +---- +CREATE CONSTRAINT book_title_year +FOR (book:Book) REQUIRE (book.title, book.publicationYear) IS UNIQUE +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + + +.Create a composite relationship property uniqueness constraint on several properties label:new[Introduced in 5.7] +====== + +.Create a constraint requiring `PREQUEL_OF` relationships to have unique combinations of `order` and `author` properties +[source, cypher] +---- +CREATE CONSTRAINT prequels +FOR ()-[prequel:PREQUEL_OF]-() REQUIRE (prequel.order, prequel.author) IS UNIQUE +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +[[create-property-uniqueness-constraint-compliant-data]] +==== Create data that complies with existing property uniqueness constraints + +.Create a node that complies with existing property uniqueness constraints +====== + +.Create a `Book` node with a unique `isbn` property +[source, cypher] +---- +CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) +---- + +.Result +[source, queryresult] +---- +Added 1 label, created 1 node, set 2 properties +---- + +====== + + +.Create a relationship that complies with existing property uniqueness constraints +====== + +.Create a `SEQUEL_OF` relationship with a unique `order` property +[source, cypher] +---- +CREATE (:Book {title: 'Spirit Walker'})-[:SEQUEL_OF {order: 1, seriesTitle: 'Chronicles of Ancient Darkness'}]->(:Book {title: 'Wolf Brother'}) +---- + +.Result +[source, queryresult] +---- +Added 2 labels, created 2 nodes, set 4 properties, created 1 relationship. +---- + +====== + +[role=label--enterprise-edition] +[[create-property-existence-constraints]] +=== Create property existence constraints + +Property existence constraints ensure that a property exists either for all nodes with a specific label or for all relationships with a specific type. +Queries that try to create new nodes of the specified label, or relationships of the specified type, without the constrained property will fail. +The same is true for queries that try to remove the mandatory property. + +* xref:constraints/managing-constraints.adoc#create-single-property-existence-constraint[] +* xref:constraints/managing-constraints.adoc#create-property-existence-constraint-compliant-data[] + + +[[create-single-property-existence-constraint]] +==== Create a single property existence constraint + +Property existence constraints on single properties are created with the following commands: + +* Node property existence constraint: `CREATE CONSTRAINT constraint_name FOR (n:Label) REQUIRE n.property IS NOT NULL`. +* Relationship property existence constraint: `CREATE CONSTRAINT constraint_name FOR ()-[r:REL_TYPE]-() REQUIRE r.property IS NOT NULL`. + +For the full command syntax to create an existence constraint, see xref:constraints/syntax.adoc#create-property-existence-constraints[Syntax -> Create property existence constraints]. + +[NOTE] +It is not possible to create composite existence constraints on several properties. + +.Create a node property existence constraint +====== + +.Create a constraint requiring `Author` nodes to have a `name` property +[source, cypher] +---- +CREATE CONSTRAINT author_name +FOR (author:Author) REQUIRE author.name IS NOT NULL +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + + +.Create a relationship property existence constraint +====== + +.Create a constraint requiring `WROTE` relationships to have a `year` property +[source, cypher] +---- +CREATE CONSTRAINT wrote_year +FOR ()-[wrote:WROTE]-() REQUIRE wrote.year IS NOT NULL +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +[[create-property-existence-constraint-compliant-data]] +==== Create data that complies with existing property existence constraints + +.Create a node that complies with existing node property existence constraints +====== + +.Create an `Author` node with a `name` property: +[source, cypher] +---- +CREATE (author:Author {name:'Virginia Woolf', surname: 'Woolf'}) +---- + +.Result +[source, queryresult] +---- +Added 1 label, created 1 node, set 2 properties +---- + +====== + + +.Create a relationship that complies with existing relationship property existence constraints +====== + +.Create a `WROTE` relationship with a `year` property +[source, cypher] +---- +CREATE (author:Author {name: 'Emily Brontë', surname: 'Brontë'})-[wrote:WROTE {year: 1847, location: 'Haworth, United Kingdom', published: true}]->(book:Book {title:'Wuthering Heights', isbn: 9789186579296}) +---- + +.Result +[source, queryresult] +---- +Added 2 labels, created 2 nodes, set 7 properties, created 1 relationship +---- + +====== + + +[role=label--enterprise-edition label--new-5.9] +[[create-property-type-constraints]] +=== Create property type constraints + +Property type constraints ensure that a property has the required data type for all nodes with a specific label or for all relationships with a specific type. +Queries that attempt to add this property with the wrong data type or modify this property in a way that changes its data type for nodes of the specified label or relationships of the specified type will fail. + +Property type constraints do not require all nodes or relationships to have the property. +Nodes or relationships without the constrained property are not subject to this rule. + +* xref:constraints/managing-constraints.adoc#create-single-property-type-constraint[] +* xref:constraints/managing-constraints.adoc#create-property-type-constraint-union-type[] +* xref:constraints/managing-constraints.adoc#type-constraints-allowed-properties[] +* xref:constraints/managing-constraints.adoc#fail-to-create-property-type-constraint-invalid-type[] +* xref:constraints/managing-constraints.adoc#create-property-type-constraint-compliant-data[] + +[[create-single-property-type-constraint]] +==== Create a single property type constraint + +Property type constraints are created with the following commands: + +* Node property type constraints: `CREATE CONSTRAINT constraint_name FOR (n:Label) REQUIRE n.property IS :: `. +* Relationship property type constraints: `CREATE CONSTRAINT constraint_name FOR ()-[r:REL_TYPE]-() REQUIRE r.property IS :: `. + +`` refers to a specific Cypher data type, such as `STRING` or `INTEGER`. +For the types that properties can be constrained by, see xref:constraints/managing-constraints.adoc#type-constraints-allowed-properties[], and for information about different data types in Cypher, see xref:values-and-types/index.adoc[]. +For the full command syntax to create a property type constraint, see xref:constraints/syntax.adoc#create-property-type-constraints[Syntax -> Create property type constraints]. + +[NOTE] +It is not possible to create composite property type constraints on several properties. + +.Create a node property type constraint +====== + +.Create a constraint requiring `title` properties on `Movie` nodes to be of type `STRING` +[source, cypher] +---- +CREATE CONSTRAINT movie_title +FOR (movie:Movie) REQUIRE movie.title IS :: STRING +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- +====== + +.Create a relationship property type constraint +====== + +.Create a constraint requiring `order` properties on `PART_OF` relationships to be of type `INTEGER` +[source, cypher] +---- +CREATE CONSTRAINT part_of +FOR ()-[part:PART_OF]-() REQUIRE part.order IS :: INTEGER +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +[role=label--new-5.11] +[[create-property-type-constraint-union-type]] +==== Create property type constraints with a union type + +A closed dynamic union allows a node or relationship property to maintain some type flexibility whilst preventing unexpected values from being stored. + +.Create a node property type constraint with a union type +====== + +.Create a constraint requiring `tagline` properties on `Movie` nodes to be either of type `STRING` or `LIST` +[source, cypher] +---- +CREATE CONSTRAINT movie_tagline +FOR (movie:Movie) REQUIRE movie.tagline IS :: STRING | LIST +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +.Create a relationship property type constraint with a union type +====== + +.Create a constraint requiring `tags` properties on `PART_OF` relationships to either of type `STRING` or `LIST` +[source, cypher] +---- +CREATE CONSTRAINT part_of_tags +FOR ()-[part:PART_OF]-() REQUIRE part.tags IS :: STRING | LIST +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +[[type-constraints-allowed-properties]] +==== Allowed types + +The allowed property types for property type constraints are: + +* `BOOLEAN` +* `STRING` +* `INTEGER` +* `FLOAT` +* `DATE` +* `LOCAL TIME` +* `ZONED TIME` +* `LOCAL DATETIME` +* `ZONED DATETIME` +* `DURATION` +* `POINT` +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* `LIST` label:new[Introduced in 5.10] +* Any closed dynamic union of the above types, e.g. `INTEGER | FLOAT | STRING`. label:new[Introduced in 5.11] + +For a complete reference describing all types available in Cypher, see the section on xref::values-and-types/property-structural-constructed.adoc#types-synonyms[types and their synonyms]. + +[[fail-to-create-property-type-constraint-invalid-type]] +==== Creating property type constraints on invalid types will fail + +.Create a node property type constraint with an invalid type +====== + +.Create a constraint requiring `imdbScore` properties on `Movie` nodes to be of type `MAP` +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT score FOR (movie:Movie) REQUIRE movie.imdbScore IS :: MAP +---- + +.Error message +[source, error] +---- +Failed to create node property type constraint: Invalid property type `MAP`. +---- + +====== + +[[create-property-type-constraint-compliant-data]] +==== Create data that complies with existing property type constraints + +.Create a node that complies with existing node property type constraint +====== + +.Create an `Movie` node with a `STRING` `title` property +[source, cypher] +---- +CREATE (movie:Movie {title:'Iron Man'}) +---- + +.Result +[source, queryresult] +---- +Added 1 label, created 1 node, set 1 properties +---- + +====== + +.Create a relationship that complies with existing relationship property type constraint +====== + +.Create a `PART_OF` relationship with an `INTEGER` `order` property +[source, cypher] +---- +MATCH (movie:Movie {title:'Iron Man'}) +CREATE (movie)-[part:PART_OF {order: 3}]->(franchise:Franchise {name:'MCU'}) +---- + +.Result +[queryresult] +---- +Added 1 label, added 1 node, created 1 relationship, set 2 properties +---- + +====== + + + +[role=label--enterprise-edition] +[[create-key-constraints]] +=== Create key constraints + +Key constraints ensure that the property exist and the property value is unique for all nodes with a specific label or all relationships with a specific type. +For composite key constraints on multiple properties, all properties must exists and the combination of property values must be unique. + +Queries that try to create new nodes of the specified label, or relationships of the specified type, without the constrained property will fail. +The same is true for queries that try to remove the mandatory property or add duplicated property values. + +* xref:constraints/managing-constraints.adoc#create-single-property-key-constraint[] +* xref:constraints/managing-constraints.adoc#create-composite-key-constraint[] +* xref:constraints/managing-constraints.adoc#create-key-constraint-compliant-data[] + +[[create-single-property-key-constraint]] +==== Create a single property key constraint + +Single property key constraints are created with the following commands: + +* Node key constraints: `CREATE CONSTRAINT constraint_name FOR (n:Label) REQUIRE n.property IS NODE KEY`. +* Relationship key constraints: `CREATE CONSTRAINT constraint_name FOR ()-[r:REL_TYPE]-() REQUIRE r.property IS RELATIONSHIP KEY`. label:new[Introduced in 5.7] + +For the full command syntax to create a key constraint, see xref:constraints/syntax.adoc#create-key-constraints[Syntax -> Create key constraints]. + +.Create a node key constraint on a single property +====== + +.Create a constraint requiring `Director` nodes to have a unique `imdbId` property as a node key. +[source, cypher] +---- +CREATE CONSTRAINT director_imdbId +FOR (director:Director) REQUIRE (director.imdbId) IS NODE KEY +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + + +.Create a relationship key constraint on a single property label:new[Introduced in 5.7] +====== + +.Create a constraint requiring `OWNS` relationships to have a unique `ownershipId` property as a relationship key +[source, cypher] +---- +CREATE CONSTRAINT ownershipId +FOR ()-[owns:OWNS]-() REQUIRE owns.ownershipId IS RELATIONSHIP KEY +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +[[create-composite-key-constraint]] +==== Create a composite key constraint + +Constraints created for multiple properties are called composite constraints. +Note that the constrained properties must be parenthesized when creating composite key constraints. + +Composite key constraints are created with the following commands: + +* Node key constraints: `CREATE CONSTRAINT constraint_name FOR (n:Label) REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS NODE KEY`. +* Relationship key constraints: `CREATE CONSTRAINT constraint_name FOR ()-[r:REL_TYPE]-() REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS RELATIONSHIP KEY`. label:new[Introduced in 5.7] + +For the full command syntax to create a key constraint, see xref:constraints/syntax.adoc#create-key-constraints[Syntax -> Create key constraints]. + +.Create a composite node key constraint on multiple properties +====== + +.Create a constraint requiring `Actor` nodes to have a unique combination of `firstname` and `surname` properties as a node key +[source, cypher] +---- +CREATE CONSTRAINT actor_fullname +FOR (actor:Actor) REQUIRE (actor.firstname, actor.surname) IS NODE KEY +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +.Create a composite relationship key constraint label on multiple properties label:new[Introduced in 5.7] +====== + +.Create a constraint requiring `KNOWS` relationships to have a unique combination of `since` and `how` properties as a relationship key +[source, cypher] +---- +CREATE CONSTRAINT knows_since_how +FOR ()-[knows:KNOWS]-() REQUIRE (knows.since, knows.how) IS RELATIONSHIP KEY +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +[[create-key-constraint-compliant-data]] +==== Create data that complies with existing key constraints + +.Create a node that complies with existing node key constraints +====== + +.Create an `Actor` node with unique `firstname` and `surname` properties +[source, cypher] +---- +CREATE (actor:Actor {firstname: 'Keanu', surname: 'Reeves'}) +---- + +.Result +[source, queryresult] +---- +Added 1 label, created 1 node, set 2 properties. +---- + +====== + + +.Create a relationship that complies with existing relationship key constraints +====== + +.Create a `KNOWS` relationship with unique `since` and `how` properties +[source, cypher] +---- +CREATE (:Actor {firstname: 'Jensen', surname: 'Ackles'})-[:KNOWS {since: 2008, how: 'coworkers', friend: true}]->(:Actor {firstname: 'Misha', surname: 'Collins'}) +---- + +.Result +[source, queryresult] +---- +Added 2 labels, created 2 nodes, set 7 properties, created 1 relationship. +---- + +====== + + +[role=label--new-5.16] +[[create-constraint-with-parameter]] +=== Create a constraint with a parameter + +All constraint types can be created with a parameterized name. + +.Create a node property uniqueness constraint using a parameter +====== + +.Parameters +[source, parameters] +---- +{ + "name": "node_uniqueness_param" +} +---- + +.Create a node property uniqueness constraint with a parameterized name +[source, cypher] +---- +CREATE CONSTRAINT $name +FOR (book:Book) REQUIRE book.prop1 IS UNIQUE +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +.Create a relationship property existence constraint using a parameter +====== + +.Parameters +[source, parameters] +---- +{ + "name": "rel_exist_param" +} +---- + +.Create a relationship property existence constraint with a parameterized name +[source, cypher] +---- +CREATE CONSTRAINT $name +FOR ()-[wrote:WROTE]-() REQUIRE wrote.published IS NOT NULL +---- + +.Result +[source, queryresult] +---- +Added 1 constraint. +---- + +====== + +[[handling-multiple-constraints]] +=== Handling multiple constraints + +Creating an already existing constraint will fail. +This includes the following scenarios: + +* Creating a constraint identical to an already existing constraint. +* Creating a constraint with a different name but on the same constraint type and same label/relationship type and property combination as an already existing constraint. +For property type constraints the property type also needs to be the same. +* Creating a constraint with the same name as an already existing constraint, regardless of what that constraint is. + +Additionally, some constraints cannot coexist and attempting to create them together will therefore fail as well. +This includes: + +* Property type constraints on the same label/relationship type and property but with different property types. +* Property uniqueness and key constraints on the same label/relationship type and property combination. + +However, some constraint types are allowed on the same label/relationship type and property combination. +For example, it is possible to have a property uniqueness and a property 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 a property existence constraint to ensure that the property exists and has the given type. + +* xref:constraints/managing-constraints.adoc#create-a-constraint-if-not-exist[] +* xref:constraints/managing-constraints.adoc#create-an-already-existing-constraint[] + +[[create-a-constraint-if-not-exist]] +==== Handling existing constraints when creating a constraint + +To avoid failing on existing constraints, `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 constraint on the same constraint type and schema, or both, already exists. +For property type constraints the property type also needs to be the same. +As of Neo4j 5.17, an informational notification is instead returned showing the existing constraint which blocks the creation. + +.Create a constraint identical to an existing constraint +====== + +.Create a constraint requiring all `SEQUEL_OF` relationships to have unique `order` properties +[source, cypher] +---- +CREATE CONSTRAINT sequels IF NOT EXISTS +FOR ()-[sequel:SEQUEL_OF]-() REQUIRE sequel.order IS UNIQUE +---- + +Because the same constraint already exists, nothing will happen: + +.Result +[source, queryresult] +---- +(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) IS UNIQUE` already exists. +---- + +====== + +.Create a relationship property uniqueness constraint when the same constraint with a different name already exists +====== + +.Create a constraint requiring all `SEQUEL_OF` relationships to have unique `order` properties +[source, cypher] +---- +CREATE CONSTRAINT new_sequels IF NOT EXISTS +FOR ()-[sequel:SEQUEL_OF]-() REQUIRE sequel.order IS UNIQUE +---- + +Because a constraint with a different name (`sequels`) on the same schema exists, nothing will happen: + +.Result +[source, queryresult] +---- +(no changes, no records) +---- + +.Notification +[source] +---- +`CREATE CONSTRAINT new_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) IS UNIQUE` already exists. +---- + +====== + +.Create a relationship property uniqueness constraint with the same name as an existing constraint of a different type +====== + +.Create a constraint requiring all `AUTHORED` relationships to have unique `name` properties +[source, cypher] +---- +CREATE CONSTRAINT author_name IF NOT EXISTS +FOR ()-[a:AUTHORED]-() REQUIRE a.name IS UNIQUE +---- + +Because a node property existence constraint named `author_name` already exists, nothing will happen: + +.Result +[source, queryresult] +---- +(no changes, no records) +---- + +.Notification +[source] +---- +`CREATE CONSTRAINT author_name IF NOT EXISTS FOR ()-[e:AUTHORED]-() REQUIRE (e.name) IS UNIQUE` has no effect. +`CONSTRAINT author_name FOR (e:Author) REQUIRE (e.name) IS NOT NULL` already exists. +---- + +====== + +[[create-an-already-existing-constraint]] +==== Creating an already existing constraint will fail + +Creating a constraint with the same name or on the same node label or relationship type and properties that are already constrained by a constraint of the same type will fail. +Property uniqueness and key constraints are also not allowed on the same schema. + +.Create a constraint identical to an existing constraint +====== + +.Create a constraint requiring all `SEQUEL_OF` relationships to have unique `order` properties, given an identical constraint already exists +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT sequels +FOR ()-[sequel:SEQUEL_OF]-() REQUIRE sequel.order IS UNIQUE +---- + +.Error message +[source, error] +---- +An equivalent constraint already exists, 'Constraint( id=5, name='sequels', type='RELATIONSHIP UNIQUENESS', schema=()-[:SEQUEL_OF {order}]-(), ownedIndex=4 )'. +---- + +[NOTE] +The constraint type will be updated to say `RELATIONSHIP PROPERTY UNIQUENESS` in a future version of Neo4j. + +====== + +.Create a constraint with a different name but on the same schema as an existing constraint +====== + +.Create a constraint requiring all `Book` nodes to have unique `isbn` properties, given that a constraint on that schema already exists +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT new_book_isbn +FOR (book:Book) REQUIRE book.isbn IS UNIQUE +---- + +.Error message +[source, error] +---- +Constraint already exists: Constraint( id=3, name='book_isbn', type='UNIQUENESS', schema=(:Book {isbn}), ownedIndex=2 ) +---- + +[NOTE] +The constraint type will be updated to say `NODE PROPERTY UNIQUENESS` in a future version of Neo4j. + +====== + +.Creating a constraint with the same name but on a different schema as an existing constraint +====== + +.Create a constraint requiring all `AUTHORED` relationships to have unique `name` properties, given that a constraint on a different schema with the same name already exists +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT author_name +FOR ()-[a:AUTHORED]-() REQUIRE a.name IS UNIQUE +---- + +.Error message +[source, error] +---- +There already exists a constraint called 'author_name'. +---- + +====== + +.Creating a property type constraint on a property when a property type constraint constraining the property to a different type already exist +====== + +.Create a constraint requiring `order` properties on `PART_OF` relationships to be of type `FLOAT`, given a constraint requiring the same properties to be of type `INTEGER` already exists +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT new_part_of +FOR ()-[part:PART_OF]-() REQUIRE part.order IS :: FLOAT +---- + + +.Error message +[source, error] +---- +Conflicting constraint already exists: Constraint( id=21, name='part_of', type='RELATIONSHIP PROPERTY TYPE', schema=()-[:PART_OF {order}]-(), propertyType=INTEGER ) +---- + +====== + +.Creating a node key constraint on the same schema as an existing property uniqueness constraint +====== + +.Create a node key constraint on the properties `title` and `publicationYear` on nodes with the `Book` label, when a property uniqueness constraint already exists on the same label and property combination +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT book_titles FOR (book:Book) REQUIRE (book.title, book.publicationYear) IS NODE KEY +---- + +.Error message +[source, error] +---- +Constraint already exists: Constraint( id=7, name='book_title_year', type='UNIQUENESS', schema=(:Book {title, publicationYear}), ownedIndex=6 ) +---- + +====== + +[[constraints-and-indexes]] +=== Constraints and indexes + +* xref:constraints/managing-constraints.adoc#constraints-and-backing-indexes[] +* xref:constraints/managing-constraints.adoc#constraint-failures-and-indexes[] + +[[constraints-and-backing-indexes]] +==== Constraints and backing indexes + +Property uniqueness constraints and key constraints are backed by xref:indexes/search-performance-indexes/managing-indexes.adoc#create-range-index[range indexes]. +This means that creating a property uniqueness or key constraint will create a range index with the same name, node label/relationship type and property combination as its owning constraint. +Single property constraints will create single property indexes and multiple property composite constraints will create xref:indexes/search-performance-indexes/using-indexes.adoc#composite-indexes[composite indexes]. + +[NOTE] +Indexes of the same index type, label/relationship type, and property combination cannot be added separately. +However, dropping a property uniqueness or key constraint will also drop its backing index. +If the backing index is still required, the index needs to be explicitly re-created. + +Property uniqueness and key constraints require an index because it allows the system to quickly check if a node with the same label and property value or a relationship with the same type and property value already exists. +Without an index, the system would need to scan all nodes with the same label, which would be slow and inefficient, especially as the graph grows. +The index makes these checks much faster by enabling direct lookups instead of scanning the entire graph. +Cypher will use the indexes with an owning constraint in the same way that it utilizes other search-performance indexes. +For more information about how indexes impact query performance, see xref:indexes/search-performance-indexes/using-indexes.adoc[]. + +These indexes are listed in the `owningConstraint` column returned by the xref:indexes/search-performance-indexes/managing-indexes.adoc#list-indexes[`SHOW INDEX`] command, and the `ownedIndex` column returned by the xref:constraints/managing-constraints.adoc#list-constraints[`SHOW CONSTRAINT`] command. + +.List constraints with backing indexes +====== + +.Query +[source, cypher, test-exclude-cols=id] +---- +SHOW CONSTRAINTS WHERE ownedIndex IS NOT NULL +---- + +.Result +[source, queryresult] +---- ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | propertyType | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 21 | "actor_fullname" | "NODE_KEY" | "NODE" | ["Actor"] | ["firstname", "surname"] | "actor_fullname" | NULL | +| 3 | "book_isbn" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "book_isbn" | NULL | +| 7 | "book_title_year" | "UNIQUENESS" | "NODE" | ["Book"] | ["title", "publicationYear"] | "book_title_year" | NULL | +| 17 | "director_imdbId" | "NODE_KEY" | "NODE" | ["Director"] | ["imdbId"] | "director_imdbId" | NULL | +| 23 | "knows_since_how" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["since", "how"] | "knows_since_how" | NULL | +| 25 | "node_uniqueness_param" | "UNIQUENESS" | "NODE" | ["Book"] | ["prop1"] | "node_uniqueness_param" | NULL | +| 19 | "ownershipId" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["OWNS"] | ["ownershipId"] | "ownershipId" | NULL | +| 9 | "prequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["PREQUEL_OF"] | ["order", "author"] | "prequels" | NULL | +| 5 | "sequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["order"] | "sequels" | NULL | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- + +====== + +.List indexes with owning constraints +====== + +.Query +[source, cypher, test-exclude-cols=id] +---- +SHOW INDEXES WHERE owningConstraint IS NOT NULL +---- + +.Result +[source, queryresult] +---- ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 20 | "actor_fullname" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Actor"] | ["firstname", "surname"] | "range-1.0" | "actor_fullname" | 2024-10-07T12:12:51.893Z | 3 | +| 2 | "book_isbn" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "book_isbn" | 2024-10-07T11:58:09.252Z | 2 | +| 6 | "book_title_year" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title", "publicationYear"] | "range-1.0" | "book_title_year" | NULL | 0 | +| 16 | "director_imdbId" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Director"] | ["imdbId"] | "range-1.0" | "director_imdbId" | NULL | 0 | +| 22 | "knows_since_how" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since", "how"] | "range-1.0" | "knows_since_how" | 2024-10-07T12:12:51.894Z | 1 | +| 24 | "node_uniqueness_param" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["prop1"] | "range-1.0" | "node_uniqueness_param" | NULL | 0 | +| 18 | "ownershipId" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["OWNS"] | ["ownershipId"] | "range-1.0" | "ownershipId" | NULL | 0 | +| 8 | "prequels" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PREQUEL_OF"] | ["order", "author"] | "range-1.0" | "prequels" | NULL | 0 | +| 4 | "sequels" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["order"] | "range-1.0" | "sequels" | 2024-10-07T11:57:12.999Z | 1 | ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- + +====== + +[NOTE] +Property existence and property type constraints are not backed by indexes. + +[[constraint-failures-and-indexes]] +==== Constraint failures and indexes + +Attempting to create any type of constraint with the same name as an existing index will fail. + +.Creating a node property type constraint with the same name as an existing index +====== + +.Create an index with the name `directors` +[source, cypher] +---- +CREATE INDEX directors FOR (director:Director) ON (director.name) +---- + +.Create a node property type constraint with the name `directors` +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT directors FOR (movie:Movie) REQUIRE movie.director IS :: STRING +---- + +.Error message +[source, error] +---- +There already exists an index called 'directors'. +---- + +====== + +Creating key or property uniqueness constraints on the same schema as an existing index will fail. + +.Creating a node property uniqueness constraint on the same schema as an existing index +====== + +.Create an index for `wordCount` properties on `Book` nodes +[source, cypher] +---- +CREATE INDEX book_word_count FOR (book:Book) ON (book.wordCount) +---- + +.Create a constraint requiring all `Book` nodes to have unique `wordCount` properties +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT word_count FOR (book:Book) REQUIRE book.wordCount IS UNIQUE +---- + +.Error message +[source, error] +---- +There already exists an index (:Book {wordCount}). +A constraint cannot be created until the index has been dropped. +---- + +====== + + +[[constraints-and-data-violation-scenarios]] +=== Constraints and data violation scenarios + +* xref:constraints/managing-constraints.adoc#create-data-that-violates-a-constraint[] +* xref:constraints/managing-constraints.adoc#removing-an-existing-constrained-property-will-fail[] +* xref:constraints/managing-constraints.adoc#modifying-property-constrained-property-will-fail[] +* xref:constraints/managing-constraints.adoc#fail-to-create-constraint-due-to-existing-data[] + +[[create-data-that-violates-a-constraint]] +==== Creating data that violates existing constraints will fail + +.Existing constraints preventing data creation +[cols="4", options="header"] +|=== +| Constraint type +| Create nodes and relationships without an existence constrained property +| Create nodes and relationships with non-unique properties/property combinations +| Create nodes and relationships with the wrong property type + +| *Property uniqueness constraint* +| +^| ❌ +| + +| *Property existence constraint* +^| ❌ +| +| + +| *Property type constraint* +| +| +^| ❌ + +| *Key constraint* +^| ❌ +^| ❌ +| + +|=== + + +.Create a node that violates a node property uniqueness constraint +====== + +.Create a `Book` node with an `isbn` property that already exists +[source, cypher, role=test-fail] +---- +CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) +---- + +.Error message +[source, error] +---- +Node(0) already exists with label `Book` and property `isbn` = '1449356265' +---- + +====== + +.Create a node that violates an existing node property existence constraint +====== + +.Create an `Author` node without a `name` property, given a property existence constraint on `:Author(name)` +[source, cypher, role=test-fail] +---- +CREATE (author:Author {surname: 'Austen'}) +---- + +.Error message +[source, error] +---- +Node(0) with label `Author` must have the property `name` +---- + +====== + +.Create a relationship that violates an existing relationship property type constraint +====== + + +.Create a `PART_OF` relationship with a `STRING` `order` property, given a property type constraint on the relationship type `PART_OF` restricting the `order` property to `INTEGER` values +[source, cypher, role=test-fail] +---- +MATCH (movie:Movie {title:'Iron Man'}), (franchise:Franchise {name:'MCU'}) +CREATE (movie)-[part:PART_OF {order: '1'}]->(franchise) +---- + +.Error message +[source, error] +---- +Relationship(0) with type `PART_OF` has property `order` of wrong type `String`. Allowed types: INTEGER +---- + +====== + + +.Create a node that violates an existing node key constraint +====== + +.Create an `Actor` node without a `firstname` property, given a node key constraint on `:Actor(firstname, surname)` +[source, cypher, role=test-fail] +---- +CREATE (actor:Actor {surname: 'Wood'}) +---- + +.Error message +[source, error] +---- +Node(0) with label `Actor` must have the properties (`firstname`, `surname`) +---- + +====== + +[[removing-an-existing-constrained-property-will-fail]] +==== Removing existence and key constrained properties will fail + +.Remove a node property existence constrained property +====== + +.Remove the `name` property from an existing `Author` node, given a property existence constraint on `:Author(name)` +[source, cypher, role=test-fail] +---- +MATCH (author:Author {name: 'Virginia Woolf'}) +REMOVE author.name +---- + +.Error message +[source, error] +---- +Node(0) with label `Author` must have the property `name` +---- + +====== + + +.Remove a node key constrained property +====== + +.Remove the `firstname` property from an existing node `Actor`, given a node key constraint on `:Actor(firstname, surname)` +[source, cypher, role=test-fail] +---- +MATCH (actor:Actor {firstname: 'Keanu', surname: 'Reeves'}) +REMOVE actor.firstname +---- + +.Error message +[source, error] +---- +Node(0) with label `Actor` must have the properties (`firstname`, `surname`) +---- + +====== + +[[modifying-property-constrained-property-will-fail]] +==== Modifying type constrained properties will fail + +.Modify a type constrained property +====== + +.Modify the `title` for the `Movie` 'Iron Man' to an `INTEGER` value, given a constraint requiring `title` properties to be of type `STRING` +[source, cypher, role=test-fail] +---- +MATCH (m:Movie {title: 'Iron Man'}) +SET m.title = 13 +---- + +.Error message +[source, error] +---- +Node(9) with label `Movie` required the property `title` to be of type `STRING`, but was of type `INTEGER`. +---- + +====== + + +[[fail-to-create-constraint-due-to-existing-data]] +==== Creating constraints when there exists conflicting data will fail + +.Existing data preventing constraint creation +[cols="4", options="header"] +|=== +| Constraint type +| Non-existing property +| Non-unique property/property combination +| Property of wrong type + +| *Property uniqueness constraint* +| +^| ❌ +| + +| *Property existence constraint* +^| ❌ +| +| + +| *Property type constraint* +| +| +^| ❌ + +| *Key constraint* +^| ❌ +^| ❌ +| + +|=== + +.Create a node property uniqueness constraint when conflicting nodes exist +====== + +.Create two `Book` nodes with the same `name` property value +[source, cypher] +---- +CREATE (:Book {isbn: '9780393972832', title: 'Moby Dick'}), + (:Book {isbn: '9780763630188', title: 'Moby Dick'}) +---- + + +.Create a constraint requiring `Book` nodes to have unique `title` properties, when there already exists two `Book` nodes with the same `title` +[source, cypher, role=test-fail] +---- +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/search-performance-indexes/managing-indexes.adoc[indexes] instead, or remove/correct the offending nodes and then re-apply the constraint. + +.Error message +[source, error] +---- +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' +---- + +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 graph. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + +.Find all offending nodes with the non-unique property values for the constraint above +[source, cypher] +---- +MATCH (book1:Book), (book2:Book) +WHERE book1.title = book2.title AND NOT book1 = book2 +RETURN book1, book2 +---- + +====== + + +.Create a relationship property existence constraint when conflicting relationships exist +====== + +.Create a constraint requiring all `WROTE` relationships to have a `language` property, when there already exists a `WROTE` relationship without a `language` property +[source, cypher, role=test-fail] +---- +CREATE CONSTRAINT wrote_language FOR ()-[wrote:WROTE]-() REQUIRE wrote.language IS NOT NULL +---- + +In this case, the constraint cannot be created because it is in conflict with the existing graph. +Remove or correct the offending relationships and then re-apply the constraint. + +.Error message +[source, error] +---- +Unable to create Constraint( type='RELATIONSHIP PROPERTY EXISTENCE', schema=()-[:WROTE {language}]-() ): +Relationship(0) with type `WROTE` must have the property `language`. Note that only the first found violation is shown. +---- + +The constraint creation fails on the first offending relationship that is found. +This does not guarantee that there are no other offending relationships in the graph. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + + +.Find all offending relationships missing the property for the constraint above +[source, cypher] +---- +MATCH ()-[wrote:WROTE]-() +WHERE wrote.language IS NULL +RETURN wrote +---- + +====== + +.Generic `MATCH` queries to find the properties preventing the creation of particular constraints: +[options="header", cols="1,1m"] +|=== +| Constraint +| Query + +| Node property uniqueness constraint +a| +[source] +---- +MATCH (n1:Label), (n2:Label) +WHERE n1.prop = n2.prop AND NOT n1 = n2 +RETURN n1, n2 +---- + +| Relationship property uniqueness constraint +a| +[source] +---- +MATCH ()-[r1:REL_TYPE]->(), ()-[r2:REL_TYPE]->() +WHERE r1.prop = r2.prop AND NOT r1 = r2 +RETURN r1, r2 +---- + +| Node property existence constraint +a| +[source] +---- +MATCH (n:Label) +WHERE n.prop IS NULL +RETURN n +---- + +| Relationship property existence constraint +a| +[source] +---- +MATCH ()-[r:REL_TYPE]->() +WHERE r.prop IS NULL +RETURN r +---- + +| Node property type constraint +a| +[source] +---- +MATCH (n:Label) +WHERE n.prop IS NOT :: +RETURN n +---- + +| Relationship property type constraint +a| +[source] +---- +MATCH ()-[r:REL_TYPE]->() +WHERE r.prop IS NOT :: +RETURN r +---- + +| Node key constraint +a| +[source] +---- +MATCH (n1:Label), (n2:Label) +WHERE n1.prop = n2.prop AND NOT n1 = n2 +UNWIND [n1, n2] AS node +RETURN node, 'non-unique' AS reason +UNION +MATCH (n:Label) +WHERE n.prop IS NULL +RETURN n AS node, 'non-existing' AS reason +---- + +| Relationship key constraint +a| +[source] +---- +MATCH ()-[r1:REL_TYPE]->(), ()-[r2:REL_TYPE]->() +WHERE r1.prop = r2.prop AND NOT r1 = r2 +UNWIND [r1, r2] AS relationship +RETURN relationship, 'non-unique' AS reason +UNION +MATCH ()-[r:REL_TYPE]->() +WHERE r.prop IS NULL +RETURN r AS relationship, 'non-existing' AS reason +---- + +|=== + +[[list-constraints]] +== SHOW CONSTRAINTS + +To list all constraints with the default output columns, use `SHOW CONSTRAINTS`. +If all columns are required, use `SHOW CONSTRAINTS YIELD *`. +For the full command syntax to list constraints, see xref:constraints/syntax.adoc#list-constraints[Syntax -> SHOW CONSTRAINTS]. + +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/managing-constraints.adoc#drop-constraint[`DROP CONSTRAINT` command]. + +[NOTE] +Listing constraints requires the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-constraints[`SHOW CONSTRAINTS` privilege]. + +.List all constraints with default output columns +====== + +.Query +[source, cypher, test-exclude-cols=id] +---- +SHOW CONSTRAINTS +---- + +.Result +[source, queryresult] +---- ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | propertyType | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 21 | "actor_fullname" | "NODE_KEY" | "NODE" | ["Actor"] | ["firstname", "surname"] | "actor_fullname" | NULL | +| 10 | "author_name" | "NODE_PROPERTY_EXISTENCE" | "NODE" | ["Author"] | ["name"] | NULL | NULL | +| 3 | "book_isbn" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "book_isbn" | NULL | +| 7 | "book_title_year" | "UNIQUENESS" | "NODE" | ["Book"] | ["title", "publicationYear"] | "book_title_year" | NULL | +| 17 | "director_imdbId" | "NODE_KEY" | "NODE" | ["Director"] | ["imdbId"] | "director_imdbId" | NULL | +| 23 | "knows_since_how" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["since", "how"] | "knows_since_how" | NULL | +| 14 | "movie_tagline" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["tagline"] | NULL | "STRING | LIST" | +| 12 | "movie_title" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["title"] | NULL | "STRING" | +| 25 | "node_uniqueness_param" | "UNIQUENESS" | "NODE" | ["Book"] | ["prop1"] | "node_uniqueness_param" | NULL | +| 19 | "ownershipId" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["OWNS"] | ["ownershipId"] | "ownershipId" | NULL | +| 13 | "part_of" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["order"] | NULL | "INTEGER" | +| 15 | "part_of_tags" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["tags"] | NULL | "STRING | LIST" | +| 9 | "prequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["PREQUEL_OF"] | ["order", "author"] | "prequels" | NULL | +| 26 | "rel_exist_param" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["published"] | NULL | NULL | +| 5 | "sequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["order"] | "sequels" | NULL | +| 11 | "wrote_year" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["year"] | NULL | NULL | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- + +====== + +.List all constraints with full details +====== + +To return the full details of the constraints on a database, use `SHOW CONSTRAINTS YIELD *` + +.List all constraints with `YIELD *` +[source, cypher, test-exclude-cols=id] +---- +SHOW CONSTRAINTS YIELD * +---- + +.Result +[source, queryresult] +---- ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | propertyType | options | createStatement | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 21 | "actor_fullname" | "NODE_KEY" | "NODE" | ["Actor"] | ["firstname", "surname"] | "actor_fullname" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `actor_fullname` FOR (n:`Actor`) REQUIRE (n.`firstname`, n.`surname`) IS NODE KEY" | +| 10 | "author_name" | "NODE_PROPERTY_EXISTENCE" | "NODE" | ["Author"] | ["name"] | NULL | NULL | NULL | "CREATE CONSTRAINT `author_name` FOR (n:`Author`) REQUIRE (n.`name`) IS NOT NULL" | +| 3 | "book_isbn" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "book_isbn" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `book_isbn` FOR (n:`Book`) REQUIRE (n.`isbn`) IS UNIQUE" | +| 7 | "book_title_year" | "UNIQUENESS" | "NODE" | ["Book"] | ["title", "publicationYear"] | "book_title_year" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `book_title_year` FOR (n:`Book`) REQUIRE (n.`title`, n.`publicationYear`) IS UNIQUE" | +| 17 | "director_imdbId" | "NODE_KEY" | "NODE" | ["Director"] | ["imdbId"] | "director_imdbId" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `director_imdbId` FOR (n:`Director`) REQUIRE (n.`imdbId`) IS NODE KEY" | +| 23 | "knows_since_how" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["since", "how"] | "knows_since_how" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `knows_since_how` FOR ()-[r:`KNOWS`]-() REQUIRE (r.`since`, r.`how`) IS RELATIONSHIP KEY" | +| 14 | "movie_tagline" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["tagline"] | NULL | "STRING | LIST" | NULL | "CREATE CONSTRAINT `movie_tagline` FOR (n:`Movie`) REQUIRE (n.`tagline`) IS :: STRING | LIST" | +| 12 | "movie_title" | "NODE_PROPERTY_TYPE" | "NODE" | ["Movie"] | ["title"] | NULL | "STRING" | NULL | "CREATE CONSTRAINT `movie_title` FOR (n:`Movie`) REQUIRE (n.`title`) IS :: STRING" | +| 25 | "node_uniqueness_param" | "UNIQUENESS" | "NODE" | ["Book"] | ["prop1"] | "node_uniqueness_param" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `node_uniqueness_param` FOR (n:`Book`) REQUIRE (n.`prop1`) IS UNIQUE" | +| 19 | "ownershipId" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["OWNS"] | ["ownershipId"] | "ownershipId" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `ownershipId` FOR ()-[r:`OWNS`]-() REQUIRE (r.`ownershipId`) IS RELATIONSHIP KEY" | +| 13 | "part_of" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["order"] | NULL | "INTEGER" | NULL | "CREATE CONSTRAINT `part_of` FOR ()-[r:`PART_OF`]-() REQUIRE (r.`order`) IS :: INTEGER" | +| 15 | "part_of_tags" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["tags"] | NULL | "STRING | LIST" | NULL | "CREATE CONSTRAINT `part_of_tags` FOR ()-[r:`PART_OF`]-() REQUIRE (r.`tags`) IS :: STRING | LIST" | +| 9 | "prequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["PREQUEL_OF"] | ["order", "author"] | "prequels" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `prequels` FOR ()-[r:`PREQUEL_OF`]-() REQUIRE (r.`order`, r.`author`) IS UNIQUE" | +| 26 | "rel_exist_param" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["published"] | NULL | NULL | NULL | "CREATE CONSTRAINT `rel_exist_param` FOR ()-[r:`WROTE`]-() REQUIRE (r.`published`) IS NOT NULL" | +| 5 | "sequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["order"] | "sequels" | NULL | {indexConfig: {}, indexProvider: "range-1.0"} | "CREATE CONSTRAINT `sequels` FOR ()-[r:`SEQUEL_OF`]-() REQUIRE (r.`order`) IS UNIQUE" | +| 11 | "wrote_year" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["year"] | NULL | NULL | NULL | "CREATE CONSTRAINT `wrote_year` FOR ()-[r:`WROTE`]-() REQUIRE (r.`year`) IS NOT NULL" | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- +====== + +[NOTE] +The `type` column returns `UNIQUENESS` for the node property uniqueness constraint and `RELATIONSHIP_UNIQUENESS` for the relationship property uniqueness constraint. +This will be updated in a future version of Neo4j. +Node property uniqueness constraints will be updated to `NODE_PROPERTY_UNIQUENESS` and relationship property uniqueness constraints to `RELATIONSHIP_PROPERTY_UNIQUENESS`. + +[[list-constraints-with-filtering]] +=== Listing constraints with filtering + +The `SHOW CONSTRAINTS` command can be filtered in various ways. +The filtering of rows can be done using constraint type keywords or a `WHERE` clause, while filtering of columns is achieved by specifying the desired columns in a `YIELD` clause. + +.List only specific constraint types +====== + +.List only key constraints +[source, cypher, test-exclude-cols=id] +---- +SHOW KEY CONSTRAINTS +---- + +.Result +[source, queryresult] +---- ++--------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | propertyType | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 21 | "actor_fullname" | "NODE_KEY" | "NODE" | ["Actor"] | ["firstname", "surname"] | "actor_fullname" | NULL | +| 17 | "director_imdbId" | "NODE_KEY" | "NODE" | ["Director"] | ["imdbId"] | "director_imdbId" | NULL | +| 23 | "knows_since_how" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["since", "how"] | "knows_since_how" | NULL | +| 19 | "ownershipId" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["OWNS"] | ["ownershipId"] | "ownershipId" | NULL | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- + +For a full list of all the constraint types (and synonyms) available in this command see xref:constraints/syntax.adoc#list-constraints[Syntax -> SHOW CONSTRAINTS]. + +====== + + +.Filtering constraints using the `WHERE` clause +====== + +.List only constraints with a `RELATIONSHIP` `entityType` +[source, cypher, test-exclude-cols=id] +---- +SHOW CONSTRAINTS +WHERE entityType = 'RELATIONSHIP' +---- + +.Result +[source, queryresult] +---- ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | propertyType | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 23 | "knows_since_how" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["KNOWS"] | ["since", "how"] | "knows_since_how" | NULL | +| 19 | "ownershipId" | "RELATIONSHIP_KEY" | "RELATIONSHIP" | ["OWNS"] | ["ownershipId"] | "ownershipId" | NULL | +| 13 | "part_of" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["order"] | NULL | "INTEGER" | +| 15 | "part_of_tags" | "RELATIONSHIP_PROPERTY_TYPE" | "RELATIONSHIP" | ["PART_OF"] | ["tags"] | NULL | "STRING | LIST" | +| 9 | "prequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["PREQUEL_OF"] | ["order", "author"] | "prequels" | NULL | +| 26 | "rel_exist_param" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["published"] | NULL | NULL | +| 5 | "sequels" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["SEQUEL_OF"] | ["order"] | "sequels" | NULL | +| 11 | "wrote_year" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["WROTE"] | ["year"] | NULL | NULL | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- + +====== + + +.Returning specific columns for all constraints +====== + +It is possible to return only specific columns of the available constraints using the `YIELD` clause: + +.List only the `name`, `type`, and `createStatement` columns +[source, cypher, test-exclude-cols=id] +---- +SHOW CONSTRAINTS +YIELD name, type, createStatement +---- + +.Result +[source, queryresult] +---- ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | type | createStatement | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| "actor_fullname" | "NODE_KEY" | "CREATE CONSTRAINT `actor_fullname` FOR (n:`Actor`) REQUIRE (n.`firstname`, n.`surname`) IS NODE KEY" | +| "author_name" | "NODE_PROPERTY_EXISTENCE" | "CREATE CONSTRAINT `author_name` FOR (n:`Author`) REQUIRE (n.`name`) IS NOT NULL" | +| "book_isbn" | "UNIQUENESS" | "CREATE CONSTRAINT `book_isbn` FOR (n:`Book`) REQUIRE (n.`isbn`) IS UNIQUE" | +| "book_title_year" | "UNIQUENESS" | "CREATE CONSTRAINT `book_title_year` FOR (n:`Book`) REQUIRE (n.`title`, n.`publicationYear`) IS UNIQUE" | +| "constraint_with_provider" | "NODE_KEY" | "CREATE CONSTRAINT `constraint_with_provider` FOR (n:`Actor`) REQUIRE (n.`surname`) IS NODE KEY" | +| "director_imdbId" | "NODE_KEY" | "CREATE CONSTRAINT `director_imdbId` FOR (n:`Director`) REQUIRE (n.`imdbId`) IS NODE KEY" | +| "knows_since_how" | "RELATIONSHIP_KEY" | "CREATE CONSTRAINT `knows_since_how` FOR ()-[r:`KNOWS`]-() REQUIRE (r.`since`, r.`how`) IS RELATIONSHIP KEY" | +| "movie_tagline" | "NODE_PROPERTY_TYPE" | "CREATE CONSTRAINT `movie_tagline` FOR (n:`Movie`) REQUIRE (n.`tagline`) IS :: STRING | LIST" | +| "movie_title" | "NODE_PROPERTY_TYPE" | "CREATE CONSTRAINT `movie_title` FOR (n:`Movie`) REQUIRE (n.`title`) IS :: STRING" | +| "node_uniqueness_param" | "UNIQUENESS" | "CREATE CONSTRAINT `node_uniqueness_param` FOR (n:`Book`) REQUIRE (n.`prop1`) IS UNIQUE" | +| "ownershipId" | "RELATIONSHIP_KEY" | "CREATE CONSTRAINT `ownershipId` FOR ()-[r:`OWNS`]-() REQUIRE (r.`ownershipId`) IS RELATIONSHIP KEY" | +| "part_of" | "RELATIONSHIP_PROPERTY_TYPE" | "CREATE CONSTRAINT `part_of` FOR ()-[r:`PART_OF`]-() REQUIRE (r.`order`) IS :: INTEGER" | +| "part_of_tags" | "RELATIONSHIP_PROPERTY_TYPE" | "CREATE CONSTRAINT `part_of_tags` FOR ()-[r:`PART_OF`]-() REQUIRE (r.`tags`) IS :: STRING | LIST" | +| "prequels" | "RELATIONSHIP_UNIQUENESS" | "CREATE CONSTRAINT `prequels` FOR ()-[r:`PREQUEL_OF`]-() REQUIRE (r.`order`, r.`author`) IS UNIQUE" | +| "rel_exist_param" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "CREATE CONSTRAINT `rel_exist_param` FOR ()-[r:`WROTE`]-() REQUIRE (r.`published`) IS NOT NULL" | +| "sequels" | "RELATIONSHIP_UNIQUENESS" | "CREATE CONSTRAINT `sequels` FOR ()-[r:`SEQUEL_OF`]-() REQUIRE (r.`order`) IS UNIQUE" | +| "wrote_year" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "CREATE CONSTRAINT `wrote_year` FOR ()-[r:`WROTE`]-() REQUIRE (r.`year`) IS NOT NULL" | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- + +====== + + +[[list-constraints-result-columns]] +=== Result columns for listing constraints + +.Listing constraints output +[options="header", width="100%", cols="4m,6a,2m"] +|=== +| 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 uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_PROPERTY_TYPE`, `RELATIONSHIP_PROPERTY_TYPE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] + +[NOTE] +`UNIQUENESS` and `RELATIONSHIP_UNIQUENESS` will be updated to say `NODE_PROPERTY_UNIQUENESS` and `RELATIONSHIP_PROPERTY_UNIQUENESS` respectively in a future version of Neo4j. +| STRING + +| entityType +| Type of entities this constraint represents (`NODE` or `RELATIONSHIP`). label:default-output[] +| STRING + +| labelsOrTypes +| The labels or relationship types of this constraint. +The list returned will only include a single value (the name of the constrained node label or relationship type). label:default-output[] +| LIST + +| properties +| The properties of this constraint. label:default-output[] +| LIST + +| ownedIndex +| The name of the index associated with the constraint or `null`, in case no index is associated with it. label:default-output[] +| STRING + +| propertyType +| The property type the property is restricted to for property type constraints, or `null` for the other constraints. +label:default-output[] label:new[Introduced in 5.9] +| 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 + +|=== + +[[drop-constraint]] +== DROP CONSTRAINT + +Constraints are dropped using the `DROP CONSTRAINT` command. +For the full command syntax to drop constraints, see xref:constraints/syntax.adoc#drop-constraint[Syntax -> DROP CONSTRAINT]. + +[NOTE] +Dropping a constraint requires the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-constraints[`DROP CONSTRAINT` privilege]. + +[[drop-constraint-by-name]] +=== Drop a constraint by name + +A constraint can be dropped using the name with the `DROP CONSTRAINT constraint_name` command. +It is the same command for all constraint types. +The name of the constraint can be found using the xref:constraints/managing-constraints.adoc#list-constraints[`SHOW CONSTRAINTS` command], given in the output column `name`. + +.Drop a constraint by name +====== + +.Drop the constraint `book_isbn` +[source, cypher] +---- +DROP CONSTRAINT book_isbn +---- + +.Result +[source, queryresult] +---- +Removed 1 constraint. +---- + +====== + +[role=label--new-5.16] +[[drop-constraint-with-parameter]] +=== Drop a constraint with a parameter + +Constraints can be dropped with a parameterized name. + +.Drop a constraint using a parameter +====== + +.Parameters +[source, parameters] +---- +{ + "name": "actor_fullname" +} +---- + +.Drop a constraint with a parameterized name +[source, cypher] +---- +DROP CONSTRAINT $name +---- + +.Result +[source, queryresult] +---- +Removed 1 constraint. +---- + +====== + +[[drop-constraint-nonexisting-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`. +This will ensure that no error is thrown. +As of Neo4j 5.17, an informational notification is returned stating that the constraint does not exist. + +.Drop a non-existing constraint +====== + +.Drop the non-existing constraint `missing_constraint_name` +[source, cypher] +---- +DROP CONSTRAINT missing_constraint_name IF EXISTS +---- + +.Result +[source, queryresult] +---- +(no changes, no records) +---- + +.Notification +[source] +---- +`DROP CONSTRAINT missing_constraint_name IF EXISTS` has no effect. `missing_constraint_name` does not exist. +---- + +====== \ No newline at end of file diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index 1a1f5a6f9..4347777d2 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -4,13 +4,21 @@ = Syntax :check-mark: icon:check[] +This page contains the syntax for creating, listing, and dropping the constraints available in Neo4j. + +More details about the syntax can be found in the link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/syntax/[Operations Manual -> Cypher syntax for administration commands]. + [[constraints-syntax-create]] -== Syntax for creating constraints +== CREATE CONSTRAINT -Best practice when creating a constraint is to give the constraint a name. +Constraints are created with the `CREATE CONSTRAINT` command. +When creating a constraint, it is recommended to provide a constraint 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. +[NOTE] +Creating a constraint requires the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-constraints[`CREATE CONSTRAINT` privilege]. + 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. @@ -18,163 +26,84 @@ 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. As of Neo4j 5.17, an informational notification is returned in case nothing happens 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. -There is no supported index configuration for range indexes. - -[NOTE] -==== -Creating a constraint requires the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-constraints[`CREATE CONSTRAINT` privilege]. -==== - -[[constraints-syntax-create-node-unique]] -=== Node property uniqueness constraint - -This command creates a property uniqueness constraint on nodes with the specified label and properties. +[[create-property-uniqueness-constraints]] +=== Create property uniqueness constraints -[source, syntax, role="noheader", indent=0] +.Syntax for creating a node property uniqueness constraint on a single property +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) REQUIRE n.propertyName IS [NODE] UNIQUE -[OPTIONS "{" option: value[, ...] "}"] ---- -[source, syntax, role="noheader", indent=0] +.Syntax for creating a composite node property uniqueness constraint on multiple properties +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] UNIQUE -[OPTIONS "{" option: value[, ...] "}"] ---- -Index provider can be specified using the `OPTIONS` clause. - -[role=label--new-5.7] -[[constraints-syntax-create-rel-unique]] -=== 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] +.Syntax for creating a relationship property uniqueness constraint on a single property label:new[Introduced in 5.7] +[source, syntax] ---- 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] +.Syntax for creating a composite relationship property uniqueness constraint on multiple properties label:new[Introduced in 5.7] +[source, syntax] ---- 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. +For examples on how to create property uniqueness constraints, see xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[Create, show, and drop constraints -> Create property uniqueness constraint]. +Property uniqueness constraints are xref:constraints/managing-constraints.adoc#constraints-and-indexes[index-backed]. [role=label--enterprise-edition] -[[constraints-syntax-create-node-exists]] -=== Node property existence constraint - -This command creates a property existence constraint on nodes with the specified label and property. +[[create-property-existence-constraints]] +=== Create property existence constraints -[source, syntax, role="noheader", indent=0] +.Syntax for creating a node property existence constraint +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) REQUIRE n.propertyName IS NOT NULL -[OPTIONS "{" "}"] ---- -[NOTE] -==== -There are no supported `OPTIONS` values for existence constraints, but an empty options map is allowed for consistency. -==== - - -[role=label--enterprise-edition] -[[constraints-syntax-create-rel-exists]] -=== Relationship property existence constraint - -This command creates a property existence constraint on relationships with the specified relationship type and property. - -[source, syntax, role="noheader", indent=0] +.Syntax for creating a relationship property existence constraint +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR ()-"["r:RELATIONSHIP_TYPE"]"-() REQUIRE r.propertyName IS NOT NULL -[OPTIONS "{" "}"] ---- -[NOTE] -==== -There are no supported `OPTIONS` values for existence constraints, but an empty options map is allowed for consistency. -==== +For examples on how to create property existence constraints, see xref:constraints/managing-constraints.adoc#create-property-existence-constraints[Create, show, and drop constraints -> Create property existence constraints]. [role=label--enterprise-edition label--new-5.9] -[[constraints-syntax-create-node-prop-type]] -=== Node property type constraint - -This command creates a property type constraint on nodes with the specified label and property. +[[create-property-type-constraints]] +=== Create property type constraints -[source, syntax, role="noheader", indent=0] +.Syntax for creating a node property type constraint +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) REQUIRE n.propertyName {[IS] :: | IS TYPED} -[OPTIONS "{" "}"] ---- -The three variations of the expression, `IS ::`, `::`, and `IS TYPED` are syntactic synonyms for the same expression. -The preferred syntax is the `IS ::` variant. - -Where `` is one of the following property types: - -* `BOOLEAN` -* `STRING` -* `INTEGER` -* `FLOAT` -* `DATE` -* `LOCAL TIME` -* `ZONED TIME` -* `LOCAL DATETIME` -* `ZONED DATETIME` -* `DURATION` -* `POINT` -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* `LIST` label:new[Introduced in 5.10] -* Any closed dynamic union of the above types, e.g. `INTEGER | FLOAT | STRING`. label:new[Introduced in 5.11] - -Allowed syntax variations of these types are listed xref::values-and-types/property-structural-constructed.adoc#types-synonyms[here]. - -[NOTE] -==== -There are no supported `OPTIONS` values for property type constraints, but an empty options map is allowed for consistency. -==== - -[role=label--enterprise-edition label--new-5.9] -[[constraints-syntax-create-rel-prop-type]] -=== Relationship property type constraint - -This command creates a property type constraint on relationships with the specified relationship type and property. - -[source, syntax, role="noheader", indent=0] +.Syntax for creating a relationship property type constraint +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR ()-"["r:RELATIONSHIP_TYPE"]"-() REQUIRE r.propertyName {[IS] :: | IS TYPED} -[OPTIONS "{" "}"] ---- The three variations of the expression, `IS ::`, `::`, and `IS TYPED` are syntactic synonyms for the same expression. @@ -206,95 +135,65 @@ Where `` is one of the following property types: * `LIST` label:new[Introduced in 5.10] * Any closed dynamic union of the above types, e.g. `INTEGER | FLOAT | STRING`. label:new[Introduced in 5.11] -Allowed syntax variations of these types are listed xref::values-and-types/property-structural-constructed.adoc#types-synonyms[here]. +Allowed syntax variations of these types are listed in xref::values-and-types/property-structural-constructed.adoc#types-synonyms[Types and their synonyms]. -[NOTE] -==== -There are no supported `OPTIONS` values for property type constraints, but an empty options map is allowed for consistency. -==== +For examples on how to create property type constraints, see xref:constraints/managing-constraints.adoc#create-property-type-constraint[Create, show, and drop constraints -> Create property type constraints]. -[role=label--enterprise-edition] -[[constraints-syntax-create-node-key]] -=== Node key constraint -This command creates a node key constraint on nodes with the specified label and properties. +[role=label--enterprise-edition] +[[create-key-constraints]] +=== Create key constraints -[source, syntax, role="noheader", indent=0] +.Syntax for creating a node key constraint on a single property +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) REQUIRE n.propertyName IS [NODE] KEY -[OPTIONS "{" option: value[, ...] "}"] ---- -[source, syntax, role="noheader", indent=0] +.Syntax for creating a composite node key constraint on multiple properties +[source, syntax] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] KEY -[OPTIONS "{" option: value[, ...] "}"] ---- -Index provider can be specified using the `OPTIONS` clause. - -[role=label--enterprise-edition label--new-5.7] -[[constraints-syntax-create-rel-key]] -=== Relationship key constraint - -This command creates a relationship key constraint on relationships with the specified relationship type and properties. - -[source, syntax, role="noheader", indent=0] +.Syntax for creating a relationship key constraint on a single property label:new[Introduced in 5.7] +[source, syntax] ---- 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] +.Syntax for creating a composite relationship key constraint on multiple properties label:new[Introduced in 5.7] +[source, syntax] ---- 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. - -[source, syntax, role="noheader", indent=0] ----- -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. -As of Neo4j 5.17, an informational notification is instead returned detailing that the constraint does not exist. - -[NOTE] -==== -Dropping a constraint requires the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-constraints[`DROP CONSTRAINT` privilege]. -==== +For examples on how to create key constraints, see xref:constraints/managing-constraints.adoc#create-key-constraints[Create, show, and drop constraints -> Create key constraints]. +Key constraints are xref:constraints/managing-constraints.adoc#constraints-and-indexes[index-backed]. -[[constraints-syntax-list]] -== Syntax for listing constraints +[[list-constraints]] +== SHOW CONSTRAINTS -List constraints in the database, either all or filtered on constraint type. +To list all constraints with the default output columns, use `SHOW CONSTRAINTS`. +If all columns are required, use `SHOW CONSTRAINTS YIELD *`. +If only specific columns are required, use `SHOW CONSTRAINTS YIELD field[, ...]`. +The `SHOW CONSTRAINTS` clause can also be filtered using the xref:clauses/where.adoc[`WHERE`] clause. [NOTE] -==== -Listing constraints requires the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-constraints[`SHOW CONSTRAINTS` privilege]. -==== +Listing constraints requires the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#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] +.Syntax to list constraints with default return columns +[source, syntax] ---- SHOW [ ALL @@ -314,9 +213,8 @@ SHOW [ [WHERE expression] ---- -To get the full set of output columns, a yield clause is needed: - -[source, syntax, role="noheader", indent=0] +.Syntax for listing constraints with full return columns +[source, syntax] ---- SHOW [ ALL @@ -338,7 +236,6 @@ YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] [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]] @@ -397,54 +294,28 @@ label:new[Introduced in 5.7] |=== +For examples on how to list constraints, see xref:constraints/managing-constraints.adoc#list-constraints[Create, show, and drop constraints -> SHOW CONSTRAINTS]. +For full details of the result columns for the `SHOW CONSTRAINTS` command, see xref:constraints/managing-constraints.adoc#list-constraints-result-columns[Create, show, and drop constraints -> Result columns for listing constraints]. -The returned columns from the show command is: - -.Listing constraints output -[options="header", width="100%", cols="4m,6a,2m"] -|=== -| 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 uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_PROPERTY_TYPE`, `RELATIONSHIP_PROPERTY_TYPE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] -| STRING +[[drop-constraint]] +== DROP CONSTRAINT -| 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 - -| properties -| The properties of this constraint. label:default-output[] -| LIST - -| ownedIndex -| The name of the index associated with the constraint or `null`, in case no index is associated with it. label:default-output[] -| STRING +Constraints are dropped using the `DROP` CONSTRAINT command. +Dropping a constraint is done by specifying the name of the constraint. -| propertyType -| The property type the property is restricted to for property type constraints, or `null` for the other constraints. -label:default-output[] label:new[Introduced in 5.9] -| STRING +[NOTE] +Dropping a constraint requires the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-constraints[`DROP CONSTRAINT` privilege]. -| 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 +.Syntax for dropping a constraint by name +[source, syntax] +---- +DROP CONSTRAINT constraint_name [IF EXISTS] +---- -|=== +This 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. +As of Neo4j 5.17, an informational notification is instead returned detailing that the constraint does not exist. +For examples on how to drop constraints, see xref:constraints/managing-constraints.adoc#drop-constraint[Create, show, and drop constraints -> DROP CONSTRAINT]. diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 2aef91c68..4256a09a7 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -16,6 +16,348 @@ 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-2025.01]] +== Neo4j 2025.01 + +=== Deprecated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CYPHER eagerAnalyzer=ir MATCH (a)-->(b) DELETE b RETURN a +---- + +[source, cypher, role="noheader"] +---- +CYPHER eagerAnalyzer=lp MATCH (a)-->(b) DELETE b RETURN a +---- +a| + +The Cypher query option `eagerAnalyzer` is deprecated and will be removed without a replacement. +Eagerness analysis is systematically performed on the logical plan regardless of the value provided. + +|=== + + +[[cypher-deprecations-additions-removals-5.26]] +== Neo4j 5.26 + +=== Deprecated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CREATE ... INDEX ... OPTIONS { indexProvider: ... } +CREATE ... CONSTRAINTS ... OPTIONS { indexProvider: ... } +---- +| Specifying an index provider in the `OPTIONS` map when creating an index or constraint is deprecated. + +This also means that the xref:indexes/semantic-indexes/vector-indexes.adoc[vector index] index provider, `vector-1.0`, is deprecated. +Use the default index provider, `vector-2.0`, instead. + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +MATCH (where {...}) +---- +---- +MATCH (...)-[where {...}]->() +---- +a| The variable named `where` (or any casing variant, like `WHERE`) used in a node or relationship pattern followed directly by a property key-value expression is deprecated. +To continue using variables with this name, use backticks to quote the variable name: + +* Node patterns: `MATCH (++`where`++ { ... })` +* Relationship patterns: `MATCH (...)-[++`where`++ { ... }]->()` + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +... + n:A +---- +---- +... + n:A&B +---- +---- +... + n:A&B\|C +---- +a| Using an unparenthesized label expression predicate as the right-hand side operand of `\+` is deprecated. +Parenthesize the label expression predicate on the right-hand side of `+`: `... + (n:A)`. + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CASE x ... WHEN is :: STRING THEN ... END +---- +a| Using a variable named `is` (or any casing variant, like `IS`) as a `WHEN` operand in a xref:queries/case.adoc#case-simple[simple `CASE`] expression is deprecated. +To continue using variables with this name in simple `CASE` expressions, use backticks to quote the variable name: `CASE x ... WHEN ++`is`++ :: STRING THEN ... END` + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CASE x ... WHEN contains + 1 THEN ... END +---- +---- +CASE x ... WHEN contains - 1 THEN ... END +---- +a| Using a variable named `contains` (or any casing variant, like `CONTAINS`) in addition or subtraction operations within a `WHEN` operand of a xref:queries/case.adoc#case-simple[simple `CASE`] expression is deprecated. +To continue using variables with this name, use backticks to quote the variable name: + +* Additions: `CASE x ... WHEN ++`contains`++ + 1 THEN ... END` +* Subtractions: `CASE x ... WHEN ++`contains`++ - 1 THEN ... END` + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CASE x ... WHEN in[1] THEN ... END +---- +---- +CASE x ... WHEN in["abc"] THEN ... END +---- +a| Using the `[]` operator on a variable named `in` (or any casing variant, like `IN`) within a `WHEN` operand of a xref:queries/case.adoc#case-simple[simple `CASE`] expression is deprecated. +To continue using variables with this name, use backticks to quote the variable name: + +* `CASE x ... WHEN ++`in`++[1] THEN ... END` +* `CASE x ... WHEN ++`in`++["abc"] THEN ... END` + + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CALL db.schema.nodeTypeProperties() YIELD propertyTypes RETURN propertyTypes; +CALL db.schema.relTypeProperties() YIELD propertyTypes RETURN propertyTypes; +---- +a| +The column `propertyTypes` currently returned by the procedures link:{neo4j-docs-base-uri}/operations-manual/current/procedures/#procedure_db_schema_nodetypeproperties[`db.schema.nodeTypeProperties()`] and link:{neo4j-docs-base-uri}/operations-manual/current/procedures/#procedure_db_schema_reltypeproperties[`db.schema.relTypeProperties()`] produces a list of strings representing the potential Java types for a given property. +In an upcoming major release of Neo4j, this will be updated to represent the possible Cypher types for that property instead. +For all available Cypher types, see the section on xref::values-and-types/property-structural-constructed.adoc#types-synonyms[types and their synonyms]. + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CREATE DATABASE db OPTIONS { seedCredentials: ..., seedConfig: ... } +---- +| The `CREATE DATABASE` option `seedCredentials` has been deprecated. +For seeding from cloud storage, it is recommended to use `CloudSeedProvider` which will read cloud credentials and configuration from standard locations. +For further information, see link:{neo4j-docs-base-uri}/operations-manual/current/clustering/databases/#cloud-seed-provider[Managing databases in a cluster -> CloudSeedProvider]. + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CREATE DATABASE db OPTIONS { storeFormat: 'standard' } + +CREATE DATABASE db OPTIONS { storeFormat: 'high_limit' } +---- +| The `standard` and `high_limit` store formats have been deprecated. +Creating databases with these formats is therefore also deprecated. +For more information on the deprecation of these formats, see link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/store-formats/#format-deprecations[Store formats -> Format deprecations]. + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +USE my.db ... + +---- +| In xref:clauses/use.adoc[`USE`] clauses, databases and aliases with unquoted `.` are deprecated unless the `.` is used to indicate that the database or alias belongs to a composite database. +Names containing `.` should be quoted using backticks. +For example, `USE `my.db`` is valid. + +|=== + + +=== Updated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +USE graph.byElementId("4:c0a65d96-4993-4b0c-b036-e7ebd9174905:0") +MATCH (n) RETURN n +---- + +| xref:functions/graph.adoc#functions-graph-by-elementid[`graph.byElementId()`] can now be used on both link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[standard and composite databases]. +Previously it could only be used on composite databases. + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +CREATE DATABASE foo TOPOLOGY $p PRIMARIES $s SECONDARIES +---- +[source, cypher, role="noheader"] +---- +ALTER DATABASE foo SET TOPOLOGY $p PRIMARIES $s SECONDARIES +---- +| The link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/standard-databases/create-databases/[`CREATE DATABASE`] and link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/standard-databases/alter-databases/[`ALTER DATABASE`] commands now accept parameters for `TOPOLOGY` configuration. + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +GRANT READ {*} ON GRAPH * FOR (n) WHERE n.createdAt > date('2024-10-25') TO regularUsers +---- +| link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/property-based-access-control/[Property-based access control] now supports xref:values-and-types/spatial.adoc[spatial] and xref:values-and-types/temporal.adoc[temporal] values. + + +a| +label:functionality[] +label:updated[] +[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 has been un-deprecated. + +|=== + + +=== New features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +MATCH (n:$($label)), + ()-[r:$($type))]->() +---- + +[source, cypher, role="noheader"] +---- +CREATE (n:$($label)), + ()-[r:$($type)]->() +---- + +[source, cypher, role="noheader"] +---- +MERGE (n:$($label)), + ()-[r:$($type)]->() +---- + +[source, cypher, role="noheader"] +---- +LOAD CSV WITH HEADERS FROM 'file:///artists-with-headers.csv' AS line +CREATE (n:$(line.label) {name: line.Name}) +---- + +| Added the ability to dynamically reference node labels and relationship types in xref:clauses/match.adoc#dynamic-match[`MATCH`], xref:clauses/create.adoc#dynamic-create[`CREATE`], and xref:clauses/merge.adoc#dynamic-merge[`MERGE`] clauses. +Also introduced the ability to specify CSV columns dynamically when using xref:clauses/load-csv.adoc#dynamic-columns[`LOAD CSV`]. +|=== + + +[[cypher-deprecations-additions-removals-5.25]] +== Neo4j 5.25 + +=== Deprecated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CREATE DATABASE db OPTIONS { existingDataSeedInstance: ... } +---- +| The `CREATE DATABASE` option `existingDataSeedInstance` has been deprecated and replaced with the option link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/standard-databases/create-databases/#manage-databases-create-database-options[`existingDataSeedServer`]. The functionality is unchanged. +|=== + +=== Updated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +CREATE (n:Label {property: 'name'}), +()-[r:REL_TYPE]->() +---- +| Neo4j's link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/store-formats/#store-format-overview[block format] now implements xref:appendix/gql-conformance/index.adoc[GQL's] limit on the maximum length of identifiers. + +The maximum limit is set to 16,383 characters in an identifier. +This means that node labels, relationship types, and property keys cannot include more than 16,383 characters. +|=== + +=== New features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +CREATE DATABASE db OPTIONS { existingDataSeedServer: ... } +---- +| The option link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/standard-databases/create-databases/#manage-databases-create-database-options[`existingDataSeedServer`] has been added to `CREATE DATABASE`. The functionality is the same as the deprecated option `existingDataSeedServer`, which this replaces. +|=== + [[cypher-deprecations-additions-removals-5.24]] == Neo4j 5.24 @@ -93,7 +435,7 @@ label:new[] SET n[$prop] = "hello world" REMOVE n[$prop] ---- -| Added the ability to dynamically reference properties in xref:clauses/set.adoc#set-dynamically-a-property[SET] and xref:clauses/remove.adoc#remove-remove-a-property-dynamically[REMOVE] clauses. +| Added the ability to dynamically reference properties in xref:clauses/set.adoc#dynamic-set-property[SET] and xref:clauses/remove.adoc#dynamic-remove-property[REMOVE] clauses. a| label:functionality[] @@ -105,7 +447,7 @@ DROP [COMPOSITE] DATABASE ... [RESTRICT \| CASCADE ALIAS[ES]] | Added the ability to drop database aliases while deleting a database. This will affect local database aliases targeting the database and constituent database aliases belonging to the composite database. -For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/standard-databases/delete-databases/#delete-databases-with-aliases[Delete a database with local database aliases targeting it] and link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/composite-databases/delete-composite-databases/#composite-databases-delete-with-aliases[Delete a composite database with constituent database aliases]. +For more information, see link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/standard-databases/delete-databases/#delete-databases-with-aliases[Delete a database with local database aliases targeting it] and link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/composite-databases/delete-composite-databases/#composite-databases-delete-with-aliases[Delete a composite database with constituent database aliases]. a| label:functionality[] @@ -133,7 +475,7 @@ SET AUTH 'native' { SET PASSWORD CHANGE REQUIRED } ---- -| Added the ability set which link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/auth-providers[auth providers] apply to a user (Enterprise Edition). +| Added the ability set which link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/auth-providers[auth providers] apply to a user (Enterprise Edition). Administration of the native (username / password) auth via the new syntax is also now supported (Community Edition). @@ -148,7 +490,7 @@ SET AUTH 'externalProviderName' { SET ID 'userIdForExternalProvider' } ---- -| Added the ability add and remove user link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/auth-providers[auth providers] via the `ALTER USER` command. +| Added the ability add and remove user link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/auth-providers[auth providers] via the `ALTER USER` command. Setting the native (username / password) auth provider via this new syntax is also supported (Community Edition), but removing any auth provider or setting a non-native auth provider is only supported in Enterprise Edition. @@ -171,7 +513,7 @@ label:new[] + SET AUTH ---- a| -New privilege that allows a user to modify user link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/auth-providers[auth providers]. +New privilege that allows a user to modify user link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/auth-providers[auth providers]. This is a sub-privilege of the `ALTER USER` privilege. Like all `GRANT`/`DENY` commands this is only available in Enterprise Edition. @@ -321,7 +663,7 @@ GRANT TRAVERSE ON GRAPH * FOR (n:Email) WHERE n.classification IS NULL TO regula ---- DENY MATCH {*} ON GRAPH * FOR (n) WHERE n.classification <> 'UNCLASSIFIED' TO regularUsers ---- -| Introduction of link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/property-based-access-control[property-based access control] for read privileges. The ability to read, traverse and match nodes based on node property values is now supported in Enterprise Edition. +| Introduction of link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/property-based-access-control[property-based access control] for read privileges. The ability to read, traverse and match nodes based on node property values is now supported in Enterprise Edition. a| label:functionality[] @@ -753,7 +1095,7 @@ GRANT LOAD ON CIDR "127.0.0.1/32" TO role DENY LOAD ON CIDR "::1/128" TO role ---- | Added the ability to grant or deny `LOAD` privilege on a CIDR range. -For more information, see the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/load-privileges/#access-control-load-cidr[Operations Manual -> The CIDR privilege]. +For more information, see the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/load-privileges/#access-control-load-cidr[Operations Manual -> The CIDR privilege]. |=== @@ -774,9 +1116,9 @@ label:deprecated[] RETURN 1 as my\u0085identifier ---- a| -The Unicode character \`\u0085` is deprecated for unescaped identifiers and will be considered as a whitespace character in the future. -To continue using it, escape the identifier by adding backticks around the identifier. -This applies to all unescaped identifiers in Cypher, such as label expressions, properties, variable names or parameters. +The Unicode character \`\u0085` is deprecated for identifiers not quoted in backticks and will be considered as a whitespace character in the future. +To continue using it, quote the identifier with backticks. +This applies to all identifiers in Cypher, such as label expressions, properties, variable names or parameters. In the given example, the quoted identifier would be \`my�identifier`. a| @@ -787,8 +1129,8 @@ label:deprecated[] RETURN 1 as my$Identifier ---- a| -The character with the Unicode representation \`\u0024` is deprecated for unescaped identifiers and will not be supported in the future. To continue using it, escape the identifier by adding backticks around the identifier. -This applies to all unescaped identifiers in Cypher, such as label expressions, properties, variable names or parameters. In the given example, the quoted identifier would be \`my$identifier`. +The character with the Unicode representation \`\u0024` is deprecated for identifiers not quoted in backticks and will not be supported in the future. To continue using it, quote the identifier with backticks. +This applies to all identifiers in Cypher, such as label expressions, properties, variable names or parameters. In the given example, the quoted identifier would be \`my$identifier`. The following Unicode Characters are deprecated in identifiers: '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007', @@ -986,7 +1328,7 @@ CALL cdc.query(from, selectors) ---- | Introduction of the Change Data Capture (CDC) feature. -For details, see link:{neo4j-docs-base-uri}/cdc/{page-version}/[Change Data Capture]. +For details, see link:{neo4j-docs-base-uri}/cdc/current/[Change Data Capture]. a| label:functionality[] @@ -1139,7 +1481,7 @@ CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS :: ---- a| -Extended xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. +Extended xref::constraints/managing-constraints.adoc#create-property-type-constraint-union-type[node and relationship property type constraints]. Closed dynamic union types (`type1 \| type2 \| ...`) are now supported, allowing for types such as: * `INTEGER \| FLOAT` @@ -1221,7 +1563,7 @@ CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS :: ---- a| -Extended xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. +Extended xref::constraints/managing-constraints.adoc#type-constraints-allowed-properties[node and relationship property type constraints]. The new supported types are: * `LIST` @@ -1379,7 +1721,7 @@ CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS :: ---- a| -Added xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. +Added xref::constraints/managing-constraints.adoc#create-property-type-constraints[node and relationship property type constraints]. The available property types are: * `BOOLEAN` @@ -1529,7 +1871,7 @@ 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. +Added relationship xref:constraints/managing-constraints.adoc#create-key-constraint[key] and xref:constraints/managing-constraints.adoc#create--property-uniqueness-constraint[property uniqueness] constraints. a| label:functionality[] @@ -1606,7 +1948,7 @@ label:new[] `server.tag` a| New functionality to change tags at runtime via `ALTER SERVER`. -More information can be found in the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/servers#alter-server-options[Operations Manual -> `ALTER SERVER` options]. +More information can be found in the link:{neo4j-docs-base-uri}/operations-manual/current/clustering/servers#alter-server-options[Operations Manual -> `ALTER SERVER` options]. a| label:functionality[] @@ -2456,7 +2798,7 @@ label:deprecated[] CREATE DATABASE databaseName.withDot ... ---- a| -Creating a database with unescaped dots in the name has been deprecated, instead escape the database name: +Creating a database with dots in the name has been deprecated, instead quote the database name using backticks: [source, cypher, role="noheader"] ---- @@ -4280,7 +4622,7 @@ EXECUTE ---- a| New Cypher commands for administering privileges for executing procedures and user defined functions. -See link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration/#access-control-dbms-administration-execute[The DBMS `EXECUTE` privileges]. +See link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-execute[The DBMS `EXECUTE` privileges]. a| label:syntax[] @@ -4548,28 +4890,28 @@ New support for `YIELD` and `WHERE` clauses to allow filtering results. a| label:functionality[] label:new[] + -link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-transaction[TRANSACTION MANAGEMENT] privileges +link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-transaction[TRANSACTION MANAGEMENT] privileges a| New Cypher commands for administering transaction management. a| label:functionality[] label:new[] + -DBMS link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration/#access-control-dbms-administration-user-management[USER MANAGEMENT] privileges +DBMS link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-user-management[USER MANAGEMENT] privileges a| New Cypher commands for administering user management. a| label:functionality[] label:new[] + -DBMS link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration/#access-control-dbms-administration-database-management[DATABASE MANAGEMENT] privileges +DBMS link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-database-management[DATABASE MANAGEMENT] privileges a| New Cypher commands for administering database management. a| label:functionality[] label:new[] + -DBMS link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/dbms-administration/#access-control-dbms-administration-privilege-management[PRIVILEGE MANAGEMENT] privileges +DBMS link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-privilege-management[PRIVILEGE MANAGEMENT] privileges a| New Cypher commands for administering privilege management. @@ -4942,21 +5284,21 @@ This Neo4j Enterprise Edition only feature involves a new runtime that has many a| label:functionality[] label:new[] + -link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/standard-databases/manage-databases/[Multi-database administration] +link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/standard-databases/manage-databases/[Multi-database administration] a| New Cypher commands for administering multiple databases. a| label:functionality[] label:new[] + -link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/[Access control] +link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/[Access control] a| New Cypher commands for administering role-based access control. a| label:functionality[] label:new[] + -link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/manage-privileges/[Fine-grained security] +link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/manage-privileges/[Fine-grained security] a| New Cypher commands for administering dbms, database, graph and sub-graph access control. @@ -4991,7 +5333,7 @@ label:new[] DROP CONSTRAINT name ---- a| -xref:constraints/syntax.adoc#constraints-syntax-drop[New command] for dropping a constraint by name, no matter the type. +xref:constraints/managing-constraints.adoc#drop-constraint[New command] for dropping a constraint by name, no matter the type. a| @@ -5103,7 +5445,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/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 +| xref:constraints/managing-constraints.adoc#create-key-constraint[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/functions/aggregating.adoc b/modules/ROOT/pages/functions/aggregating.adoc index 2976c3666..19c0f3351 100644 --- a/modules/ROOT/pages/functions/aggregating.adoc +++ b/modules/ROOT/pages/functions/aggregating.adoc @@ -17,7 +17,7 @@ To learn more about how Cypher handles aggregations performed on zero rows, refe The following graph is used for the examples below: -image::graph_aggregating_functions.svg[role="middle", width="700"] +image::graph-aggregating-functions.svg[Graph example connecting Person and Movie nodes,role=popup,width=500] To recreate the graph, run the following query against an empty Neo4j database: @@ -63,11 +63,13 @@ CREATE ====== .Query +// tag::functions_aggregating_avg[] [source, cypher] ---- MATCH (p:Person) RETURN avg(p.age) ---- +// end::functions_aggregating_avg[] The average of all the values in the property `age` is returned: @@ -87,11 +89,13 @@ The average of all the values in the property `age` is returned: ====== .Query +// tag::functions_aggregating_duration_avg[] [source, cypher] ---- UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur RETURN avg(dur) ---- +// end::functions_aggregating_duration_avg[] The average of the two supplied `DURATION` values is returned: @@ -133,11 +137,13 @@ The average of the two supplied `DURATION` values is returned: ====== .Query +// tag::functions_aggregating_collect[] [source, cypher] ---- MATCH (p:Person) RETURN collect(p.age) ---- +// end::functions_aggregating_collect[] All the values are collected and returned in a single list: @@ -189,11 +195,13 @@ The function `count(*)` can be used to return the number of nodes; for example, ====== .Query +// tag::functions_aggregating_count[] [source, cypher] ---- MATCH (p:Person {name: 'Keanu Reeves'})-->(x) RETURN labels(p), p.age, count(*) ---- +// end::functions_aggregating_count[] The labels and `age` property of the start node `Keanu Reeves` and the number of nodes related to it are returned: @@ -248,11 +256,13 @@ Instead of simply returning the number of rows with `count(*)`, the function `co ====== .Query +// tag::functions_aggregating_count_as_expression[] [source, cypher] ---- MATCH (p:Person) RETURN count(p.age) ---- +// end::functions_aggregating_count_as_expression[] The number of nodes with the label `Person` and a property `age` is returned: (To calculate the sum, use `sum(n.age)`) @@ -302,8 +312,8 @@ The `Guy Pearce` node will, therefore, get counted twice when not using `DISTINC | friendOfFriend.name | count(friendOfFriend) | count(ALL friendOfFriend) | count(DISTINCT friendOfFriend) | "Guy Pearce" | 2 | 2 | 1 -2+d|Rows: 1 +4+d|Rows: 1 |=== ====== @@ -389,11 +399,13 @@ The highest of all the lists in the set -- in this case, the list `[1, 2]` -- is ====== .Query +// tag::functions_aggregating_max[] [source, cypher] ---- MATCH (p:Person) RETURN max(p.age) ---- +// end::functions_aggregating_max[] The highest of all the values in the property `age` is returned: @@ -486,11 +498,13 @@ The lowest of all the values in the set -- in this case, the list `['a', 'c', 23 ====== .Query +// tag::functions_aggregating_min[] [source, cypher] ---- MATCH (p:Person) RETURN min(p.age) ---- +// end::functions_aggregating_min[] The lowest of all the values in the property `age` is returned: @@ -532,11 +546,13 @@ The lowest of all the values in the property `age` is returned: ====== .Query +// tag::functions_aggregating_percentile_cont[] [source, cypher] ---- MATCH (p:Person) RETURN percentileCont(p.age, 0.4) ---- +// end::functions_aggregating_percentile_cont[] The 40th percentile of the values in the property `age` is returned, calculated with a weighted average: @@ -579,11 +595,13 @@ The 40th percentile of the values in the property `age` is returned, calculated ====== .Query +// tag::functions_aggregating_percentile_disc[] [source, cypher] ---- MATCH (p:Person) RETURN percentileDisc(p.age, 0.5) ---- +// end::functions_aggregating_percentile_disc[] The 50th percentile of the values in the property `age` is returned: @@ -625,12 +643,14 @@ The 50th percentile of the values in the property `age` is returned: ====== .Query +// tag::functions_aggregating_stdev[] [source, cypher] ---- MATCH (p:Person) WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss'] RETURN stDev(p.age) ---- +// end::functions_aggregating_stdev[] The standard deviation of the values in the property `age` is returned: @@ -672,12 +692,14 @@ The standard deviation of the values in the property `age` is returned: ====== .Query +// tag::functions_aggregating_stdevp[] [source, cypher] ---- MATCH (p:Person) WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss'] RETURN stDevP(p.age) ---- +// end::functions_aggregating_stdevp[] The population standard deviation of the values in the property `age` is returned: @@ -719,11 +741,13 @@ The population standard deviation of the values in the property `age` is returne ====== .Query +// tag::functions_aggregating_sum[] [source, cypher] ---- MATCH (p:Person) RETURN sum(p.age) ---- +// end::functions_aggregating_sum[] The sum of all the values in the property `age` is returned: @@ -744,11 +768,13 @@ The sum of all the values in the property `age` is returned: ====== .Query +// tag::functions_aggregating_sum_duration[] [source, cypher] ---- UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur RETURN sum(dur) ---- +// end::functions_aggregating_sum_duration[] The sum of the two supplied durations is returned: diff --git a/modules/ROOT/pages/functions/database.adoc b/modules/ROOT/pages/functions/database.adoc index 929d72d90..54c183b3c 100644 --- a/modules/ROOT/pages/functions/database.adoc +++ b/modules/ROOT/pages/functions/database.adoc @@ -26,11 +26,13 @@ ====== .Query +// tag::functions_database_name_from_element_id[] [source, cypher, indent=0] ---- WITH "2:efc7577d-022a-107c-a736-dbcdfc189c03:0" AS eid RETURN db.nameFromElementId(eid) AS name ---- +// end::functions_database_name_from_element_id[] Returns the name of the database which the element id belongs to. diff --git a/modules/ROOT/pages/functions/graph.adoc b/modules/ROOT/pages/functions/graph.adoc index 8ac1fcd0e..81f176db1 100644 --- a/modules/ROOT/pages/functions/graph.adoc +++ b/modules/ROOT/pages/functions/graph.adoc @@ -16,7 +16,7 @@ .Considerations |=== -| `graph.names()` is only supported on link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite databases]. +| `graph.names()` is only supported on link:{neo4j-docs-base-uri}/operations-manual/current/composite-databases[composite databases]. |=== .+graph.names()+ @@ -34,10 +34,12 @@ CREATE ALIAS composite.third FOR DATABASE dbc; ---- .Query +// tag::functions_graph_names[] [source, cypher, indent=0] ---- RETURN graph.names() AS name ---- +// end::functions_graph_names[] The names of all graphs on the current composite database are returned. @@ -69,8 +71,8 @@ The names of all graphs on the current composite database are returned. .Considerations |=== -| `graph.propertiesByName()` is only supported on link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite databases]. -| The properties in the returned `MAP` are set on the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/aliases/manage-aliases-standard-databases/[alias]that adds the graph as a constituent of a composite database. +| `graph.propertiesByName()` is only supported on link:{neo4j-docs-base-uri}/operations-manual/current/composite-databases[composite databases]. +| The properties in the returned `MAP` are set on the link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/aliases/manage-aliases-standard-databases/[alias]that adds the graph as a constituent of a composite database. |=== .+graph.propertiesByName()+ @@ -91,11 +93,13 @@ CREATE ALIAS composite.third FOR DATABASE dbc ---- .Query +// tag::functions_graph_properties_by_name[] [source, cypher, indent=0] ---- UNWIND graph.names() AS name RETURN name, graph.propertiesByName(name) AS props ---- +// end::functions_graph_properties_by_name[] Properties for all graphs on the current composite database are returned. @@ -117,7 +121,7 @@ Properties for all graphs on the current composite database are returned. UNWIND graph.names() AS name WITH name, graph.propertiesByName(name) AS props WHERE "A" IN props.tags -CALL { +CALL () { USE graph.byName(name) MATCH (n) RETURN n @@ -127,6 +131,11 @@ RETURN n Returns all nodes from a subset of graphs that have a `tags` property containing `"A"`. +[NOTE] +The above query uses an empty xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause]: `CALL () { ... }` (introduced in Neo4j 5.23). +If you are using an older version of Neo4j, use `CALL { ... }` instead. +For more information, see xref:subqueries/call-subquery.adoc#import-variables[CALL subqueries -> Importing variables]. + ====== [[functions-graph-byname]] @@ -135,29 +144,42 @@ Returns all nodes from a subset of graphs that have a `tags` property containing .Details |=== | *Syntax* 3+| `graph.byName(name)` -| *Description* 3+| Returns the graph reference of the given name. It is only supported in the `USE` clause, on composite databases. +| *Description* 3+| Returns the graph reference of the given name. .2+| *Arguments* | *Name* | *Type* | *Description* | `name` | `STRING` | The name of the graph to be resolved. | *Returns* 3+| `GRAPH` |=== + +.Considerations +|=== +| `graph.byName()` is only supported in the xref:clauses/use.adoc[`USE`] clause, on composite databases. +|=== + .+graph.byName()+ ====== .Query -[source, cypher, indent=0] +// tag::functions_graph_by_name[] +[source, cypher, role=noplay] ---- UNWIND graph.names() AS graphName -CALL { +CALL () { USE graph.byName(graphName) MATCH (n) RETURN n } RETURN n ---- +// end::functions_graph_by_name[] Returns all nodes from all graphs on the current composite database. +[NOTE] +The above query uses an empty xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause]: `CALL () { ... }` (introduced in Neo4j 5.23). +If you are using an older version of Neo4j, use `CALL { ... }` instead. +For more information, see xref:subqueries/call-subquery.adoc#import-variables[CALL subqueries -> Importing variables]. + ====== [role=label--new-5.13] @@ -167,7 +189,7 @@ Returns all nodes from all graphs on the current composite database. .Details |=== | *Syntax* 3+| `graph.byElementId(elementId)` -| *Description* 3+| Returns the graph reference with the given element id. It is only supported in the `USE` clause, on composite databases. +| *Description* 3+| Returns the graph reference with the given element id. .2+| *Arguments* | *Name* | *Type* | *Description* | `elementId` | `STRING` | An element id of a node or relationship. | *Returns* 3+| `GRAPH` @@ -176,6 +198,10 @@ Returns all nodes from all graphs on the current composite database. .Considerations |=== | If the constituent database is not a standard database in the DBMS, an error will be thrown. +| `graph.byElementId()` is only supported in the xref:clauses/use.adoc[`USE`] clause. +| As of Neo4j 5.26, `graph.byElementId()` is supported on both link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[standard and composite databases]. +On earlier versions, it is only supported on composite databases. +| On a standard database, a `USE` clause with `graph.byElementId()` cannot be combined with other `USE` clauses unless the subsequent `USE` clauses reference the same element id. |=== .+graph.byElementId()+ @@ -184,10 +210,12 @@ Returns all nodes from all graphs on the current composite database. In this example, it is assumed that the DBMS contains a composite database constituent, which contains the element id `4:c0a65d96-4993-4b0c-b036-e7ebd9174905:0`. .Query +// tag::functions_graph_by_element_id[] [source, cypher, role=test-skip] ---- USE graph.byElementId("4:c0a65d96-4993-4b0c-b036-e7ebd9174905:0") MATCH (n) RETURN n ---- +// end::functions_graph_by_element_id[] ====== diff --git a/modules/ROOT/pages/functions/index.adoc b/modules/ROOT/pages/functions/index.adoc index 315171f5c..1e80dd74a 100644 --- a/modules/ROOT/pages/functions/index.adoc +++ b/modules/ROOT/pages/functions/index.adoc @@ -208,7 +208,7 @@ LOAD CSV functions can be used to get information about the file that is process [[header-query-functions-logarithmic]] -**xref::functions/mathematical-logarithmic.adoc[Logarithmic functions]** +== Logarithmic functions These functions all operate on numerical expressions only, and will return an error if used on any other values. @@ -573,7 +573,9 @@ These functions are used to specify 2D or 3D points in a geographic or cartesian 1.1+| xref::functions/spatial.adoc#functions-distance[`point.distance()`] | `point.distance(from :: POINT, to :: POINT) :: FLOAT` -| Returns a `FLOAT` representing the geodesic distance between any two points in the same CRS. +| Returns a `FLOAT` representing the distance between any two points in the same CRS. +If the points are in the WGS 84 CRS, the function returns the geodesic distance (i.e., the shortest path along the curved surface of the Earth). +If the points are in a Cartesian CRS, the function returns the Euclidean distance (i.e., the shortest straight-line distance in a flat, planar space). 1.1+| xref::functions/spatial.adoc#functions-withinBBox[`point.withinBBox()`] | `point.withinBBox(point :: POINT, lowerLeft :: POINT, upperRight :: POINT) :: BOOLEAN` @@ -746,12 +748,12 @@ There are two main types of functions that can be developed and used: | Scalar | For each row the function takes parameters and returns a result. | xref::functions/user-defined.adoc#query-functions-udf[Using UDF] -| link:{neo4j-docs-base-uri}/java-reference/{page-version}/extending-neo4j/functions#extending-neo4j-functions[Extending Neo4j (UDF)] +| link:{neo4j-docs-base-uri}/java-reference/current/extending-neo4j/functions#extending-neo4j-functions[Extending Neo4j (UDF)] | Aggregating | Consumes many rows and produces an aggregated result. | xref::functions/user-defined.adoc#query-functions-user-defined-aggregation[Using aggregating UDF] -| link:{neo4j-docs-base-uri}/java-reference/{page-version}/extending-neo4j/aggregation-functions#extending-neo4j-aggregation-functions[Extending Neo4j (Aggregating UDF)] +| link:{neo4j-docs-base-uri}/java-reference/current/extending-neo4j/aggregation-functions#extending-neo4j-aggregation-functions[Extending Neo4j (Aggregating UDF)] |=== diff --git a/modules/ROOT/pages/functions/list.adoc b/modules/ROOT/pages/functions/list.adoc index 1ff0cd547..ffaa0ddd0 100644 --- a/modules/ROOT/pages/functions/list.adoc +++ b/modules/ROOT/pages/functions/list.adoc @@ -13,7 +13,7 @@ Further details and examples of lists may be found in xref::values-and-types/lis The following graph is used for the examples below: -image::graph_list_functions.svg[role="middle", width="700"] +image::graph-list-functions.svg[Example graph connecting people after their roles as administrator, designer, and developer,role=popup,width=500] To recreate the graph, run the following query against an empty Neo4j database: @@ -56,11 +56,13 @@ CREATE ====== .Query +// tag::functions_list_keys[] [source, cypher] ---- MATCH (a) WHERE a.name = 'Alice' RETURN keys(a) ---- +// end::functions_list_keys[] A `LIST` containing the names of all the properties on the node bound to `a` is returned. @@ -103,11 +105,13 @@ A `LIST` containing the names of all the properties on the node bound to ====== .Query +// tag::functions_list_labels[] [source, cypher] ---- MATCH (a) WHERE a.name = 'Alice' RETURN labels(a) ---- +// end::functions_list_labels[] A `LIST` containing all the labels of the node bound to `a` is returned. @@ -148,12 +152,14 @@ A `LIST` containing all the labels of the node bound to `a` is returned. ====== .Query +// tag::functions_list_nodes[] [source, cypher] ---- MATCH p = (a)-->(b)-->(c) WHERE a.name = 'Alice' AND c.name = 'Eskil' RETURN nodes(p) ---- +// end::functions_list_nodes[] A `LIST` containing all the nodes in the path `p` is returned. @@ -197,10 +203,12 @@ The only exception where the range does not contain `start` are empty ranges. ====== .Query +// tag::functions_list_range[] [source, cypher] ---- RETURN range(0, 10), range(2, 18, 3), range(0, 5, -1) ---- +// end::functions_list_range[] Three lists of numbers in the given ranges are returned. @@ -237,12 +245,14 @@ This function is analogous to the `fold` or `reduce` method in functional langua ====== .Query +// tag::functions_list_reduce[] [source, cypher] ---- MATCH p = (a)-->(b)-->(c) WHERE a.name = 'Alice' AND b.name = 'Bob' AND c.name = 'Daniel' RETURN reduce(totalAge = 0, n IN nodes(p) | totalAge + n.age) AS reduction ---- +// end::functions_list_reduce[] The `age` property of all `NODE` values in the `PATH` are summed and returned as a single value. @@ -283,12 +293,14 @@ The `age` property of all `NODE` values in the `PATH` are summed and returned as ====== .Query +// tag::functions_list_relationships[] [source, cypher] ---- MATCH p = (a)-->(b)-->(c) WHERE a.name = 'Alice' AND c.name = 'Eskil' RETURN relationships(p) ---- +// end::functions_list_relationships[] A `LIST` containing all the `RELATIONSHIP` values in the `PATH` `p` is returned. @@ -330,11 +342,13 @@ A `LIST` containing all the `RELATIONSHIP` values in the `PATH` `p ====== .Query +// tag::functions_list_reverse[] [source, cypher] ---- WITH [4923,'abc',521, null, 487] AS ids RETURN reverse(ids) ---- +// end::functions_list_reverse[] .Result [role="queryresult",options="header,footer",cols="1*` comprising all but the first element of the `likedColors` property are returned. @@ -414,12 +430,14 @@ The property named `likedColors` and a `LIST` comprising all but the first ====== .Query +// tag::functions_list_to_boolean_list[] [source, cypher, indent=0] ---- RETURN toBooleanList(null) as noList, toBooleanList([null, null]) as nullsInList, toBooleanList(['a string', true, 'false', null, ['A','B']]) as mixedList ---- +// end::functions_list_to_boolean_list[] .Result [role="queryresult",options="header,footer",cols="3*(b) @@ -69,6 +71,7 @@ WHERE AND all(x IN nodes(p) WHERE x.age < 60) RETURN p ---- +// end::functions_predicate_all[] All nodes in the returned paths will have a property `age` with a value lower than `60`: @@ -84,6 +87,23 @@ image::predicate_function_example.svg[width="300",role="middle"] |=== +.`all()` on an empty `LIST` +[source, cypher] +---- +WITH [] as emptyList +RETURN all(i in emptyList WHERE true) as allTrue, all(i in emptyList WHERE false) as allFalse +---- + +.Result +[role="queryresult",options="header,footer",cols="2*()) AS has_acted_in_rel ---- +// end::functions_predicate_exists[] 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. @@ -209,12 +252,14 @@ For information about the `EXISTS` subquery, which is more versatile than the `e ====== .Query +// tag::functions_predicate_is_empty[] [source, cypher] ---- MATCH (p:Person) WHERE NOT isEmpty(p.nationality) RETURN p.name, p.nationality ---- +// end::functions_predicate_is_empty[] This query returns every `Person` node in the graph with a set `nationality` property value (i.e., all `Person` nodes except for `Jessica Chastain`): @@ -304,12 +349,14 @@ xref:syntax/operators.adoc#cypher-comparison[`IS NULL` or `IS NOT NULL`] should .Considerations |=== | `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. +| `none()` returns `true` if `list` is empty because there are no elements to violate the `predicate`. |=== .+none()+ ====== .Query +// tag::functions_predicate_none[] [source, cypher, indent=0] ---- MATCH p = (n)-[*]->(b) @@ -318,6 +365,7 @@ WHERE AND none(x IN nodes(p) WHERE x.age > 60) RETURN p ---- +// end::functions_predicate_none[] No node in the returned path has an `age` property with a greater value than `60`: @@ -335,6 +383,23 @@ image::predicate_function_example.svg[width="300",role="middle"] |=== +.`none()` on an empty `LIST` +[source, cypher] +---- +WITH [] as emptyList +RETURN none(i IN emptyList WHERE true) as noneTrue, none(i IN emptyList WHERE false) as noneFalse +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(b) @@ -369,6 +436,7 @@ WHERE AND single(x IN nodes(p) WHERE x.nationality = 'Northern Irish') RETURN p ---- +// end::functions_predicate_single[] In every returned path there is exactly one node which has the `nationality` property value `Northern Irish`: @@ -382,4 +450,21 @@ In every returned path there is exactly one node which has the `nationality` pro |=== +.`single()` on an empty `LIST` +[source, cypher] +---- +WITH [] as emptyList +RETURN single(i IN emptyList WHERE true) as singleTrue, single(i IN emptyList WHERE false) as singleFalse +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(b)-->(c) WHERE a.name = 'Alice' RETURN length(p) ---- +// end::functions_scalar_length[] The length of the path `p` is returned. @@ -553,10 +571,12 @@ The null value is returned as the two parameters are equivalent. ====== .Query +// tag::functions_scalar_null_if[] [source, cypher, indent=0] ---- RETURN nullIf("abc", "def") ---- +// end::functions_scalar_null_if[] The first parameter, "abc", is returned, as the two parameters are not equivalent. @@ -627,11 +647,13 @@ RETURN a.name AS name, coalesce(nullIf(a.eyes, "Brown"), "Hazel") AS eyeColor ====== .Query +// tag::functions_scalar_properties[] [source, cypher, indent=0] ---- CREATE (p:Person {name: 'Stefan', city: 'Berlin'}) RETURN properties(p) ---- +// end::functions_scalar_properties[] .Result [role="queryresult",options="header,footer",cols="1*() WHERE n.name = 'Alice' RETURN type(r) ---- +// end::functions_scalar_type[] The relationship type of `r` is returned. @@ -1205,12 +1249,12 @@ Future releases of Cypher may include updates to the current type system. This can include the introduction of new types and subtypes of already supported types. If a new type is introduced, it will be returned by the `valueType()` function as soon as it is released. However, if a more precise subtype of a previously supported type is introduced, it would be considered a breaking change. -As a result, any new subtypes introduced after the release of Neo4j 5.13 will not be returned by the `valueType()` function until the following major release (Neo4j 6.0). +As a result, any new subtypes introduced after the release of Neo4j 5.13 will not be returned by the `valueType()` function until the next major release of Neo4j. -For example, the function currently returns `"FLOAT"`, but if a more specific `FLOAT` type was added, e.g. `FLOAT32`, this would be considered more specific and not be returned until Neo4j 6.0. -As a result,`"FLOAT"` would continue to be returned for any `FLOAT32` values until the release of Neo4j 6.0. +For example, the function currently returns `"FLOAT"`, but if a more specific `FLOAT` type was added, e.g. `FLOAT32`, this would be considered more specific and not be returned until the next major release of Neo4j. +As a result,`"FLOAT"` would continue to be returned for any `FLOAT32` values until the next major release. -With this in mind, the below list contains all supported types (as of Neo4j 5.13) displayed by the `valueType()` function until the release of Neo4j 6.0: +With this in mind, the below list contains all supported types (as of Neo4j 5.13) displayed by the `valueType()` function until the next major release of Neo4j: * Predefined types ** `NOTHING` @@ -1245,11 +1289,13 @@ See the xref::values-and-types/type-predicate.adoc[type predicate expression] fo ====== .Query +// tag::functions_scalar_value_type[] [source, cypher, indent=0] ---- UNWIND ["abc", 1, 2.0, true, [date()]] AS value RETURN valueType(value) AS result ---- +// end::functions_scalar_value_type[] .Result [role="queryresult",options="header,footer",cols="1*(o:Office) @@ -343,6 +350,7 @@ WITH point({longitude: o.longitude, latitude: o.latitude}) AS officePoint RETURN round(point.distance(trainPoint, officePoint)) AS travelDistance ---- +// end::functions_spatial_point_wgs_84_2d[] The distance between the train station in Copenhagen and the Neo4j office in Malmo is returned. @@ -413,6 +421,7 @@ If `null` is provided as one or both of the arguments, `null` is returned. ====== .Query +// tag::functions_spatial_point_wgs_84_3d[] [source, cypher] ---- WITH @@ -420,6 +429,7 @@ WITH point({x: 10, y: 10, crs: 'cartesian'}) AS upperRight RETURN point.withinBBox(point({x: 5, y: 5, crs: 'cartesian'}), lowerLeft, upperRight) AS result ---- +// end::functions_spatial_point_wgs_84_3d[] Checking if a point in _Cartesian_ CRS is contained in the bounding box. @@ -440,6 +450,7 @@ Checking if a point in _Cartesian_ CRS is contained in the bounding box. ====== .Query +// tag::functions_spatial_point_cartesian_2d[] [source, cypher] ---- WITH @@ -449,6 +460,7 @@ MATCH (t:TrainStation) WHERE point.withinBBox(point({longitude: t.longitude, latitude: t.latitude}), lowerLeft, upperRight) RETURN count(t) ---- +// end::functions_spatial_point_cartesian_2d[] Finds all train stations contained in a bounding box around Copenhagen. @@ -496,6 +508,7 @@ A bounding box that crosses the 180th meridian. ====== .Query +// tag::functions_spatial_point_cartesian_3d[] [source, cypher] ---- RETURN @@ -505,6 +518,7 @@ RETURN point({longitude: 57.0, latitude: 13.0}) ) AS in ---- +// end::functions_spatial_point_cartesian_3d[] If `null` is provided as any of the arguments, `null` is returned. diff --git a/modules/ROOT/pages/functions/string.adoc b/modules/ROOT/pages/functions/string.adoc index 68725bd94..fef3a40d7 100644 --- a/modules/ROOT/pages/functions/string.adoc +++ b/modules/ROOT/pages/functions/string.adoc @@ -48,10 +48,12 @@ See also xref::syntax/operators.adoc#query-operators-string[String operators]. ====== .Query +// tag::functions_string_btrim[] [source, cypher, indent=0] ---- RETURN btrim(' hello '), btrim('xxyyhelloxyxy', 'xy') ---- +// end::functions_string_btrim[] .Result [role="queryresult",options="header,footer",cols="2* User-defined functions]. +For developing and deploying user-defined functions in Neo4j, see link:{neo4j-docs-base-uri}/java-reference/current/extending-neo4j/functions#extending-neo4j-functions[Extending Neo4j -> User-defined functions]. .Call a user-defined function diff --git a/modules/ROOT/pages/functions/vector.adoc b/modules/ROOT/pages/functions/vector.adoc index 3db5ecef0..cd2cea108 100644 --- a/modules/ROOT/pages/functions/vector.adoc +++ b/modules/ROOT/pages/functions/vector.adoc @@ -35,6 +35,8 @@ For more details, see the {link-vector-indexes}#similarity-functions[vector inde | Both vectors must be of the same dimension. | Both vectors must be {link-vector-indexes}#indexes-vector-similarity-cosine[*valid*] with respect to cosine similarity. | The implementation is identical to that of the latest available vector index provider (`vector-2.0`). +| `vector.similarity.cosine()` returns the neighborhood of nodes along with their respective cosine similarity scores, sorted in descending order of similarity. +The similarity score range from `0` and `1`, with scores closer to `1` indicating a higher degree of similarity between the indexed vector and the query vector. |=== @@ -63,6 +65,8 @@ For more details, see the {link-vector-indexes}#similarity-functions[vector inde | Both vectors must be of the same dimension. | Both vectors must be {link-vector-indexes}#indexes-vector-similarity-euclidean[*valid*] with respect to Euclidean similarity. | The implementation is identical to that of the latest available vector index provider (`vector-2.0`). +| `vector.similarity.euclidean()` returns the neighborhood of nodes along with their respective Euclidean similarity scores, sorted in descending order of similarity. +The similarity score range from `0` and `1`, with scores closer to `1` indicating a higher degree of similarity between the indexed vector and the query vector. |=== diff --git a/modules/ROOT/pages/genai-integrations.adoc b/modules/ROOT/pages/genai-integrations.adoc index 5168f0d01..d229abf79 100644 --- a/modules/ROOT/pages/genai-integrations.adoc +++ b/modules/ROOT/pages/genai-integrations.adoc @@ -23,7 +23,7 @@ The GenAI plugin is enabled by default in Neo4j Aura. The plugin needs to be installed on self-managed instances. This is done by moving the `neo4j-genai.jar` file from `/products` to `/plugins` in the Neo4j home directory, or, if you are using Docker, by starting the Docker container with the extra parameter `--env NEO4J_PLUGINS='["genai"]'`. -For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/plugins/[Operations Manual -> Configure plugins]. +For more information, see link:{neo4j-docs-base-uri}/operations-manual/current/configuration/plugins/[Operations Manual -> Configure plugins]. [NOTE] Prior to Neo4j 5.23, the GenAI plugin was only available on Neo4j Enterprise Edition. @@ -39,7 +39,7 @@ The graph contains 28863 nodes and 332522 relationships. There are 9083 `Movie` nodes with a `plot` and `title` property. To recreate the graph, download and import this link:https://github.com/neo4j-graph-examples/recommendations/blob/main/data/recommendations-embeddings-50.dump[dump file] to an empty Neo4j database (running version 5.17 or later). -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. +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/current/backup-restore/restore-dump/[on-prem] instances. [NOTE] The embeddings on this are generated using link:https://platform.openai.com/docs/guides/embeddings[OpenAI] (model `text-embedding-ada-002`), producing 1536-dimensional vectors. @@ -180,15 +180,15 @@ CALL db.create.setNodeVectorProperty(moviesList[index], 'embedding', vector) // [source, cypher, role=test-skip] ---- MATCH (m:Movie WHERE m.plot IS NOT NULL) -WITH collect(m) AS moviesList // <1> +WITH collect(m) AS moviesList, // <1> count(*) AS total, 100 AS batchSize // <2> -UNWIND range(0, total, batchSize) AS batchStart // <3> +UNWIND range(0, total-1, batchSize) AS batchStart // <3> CALL (moviesList, batchStart, batchSize) { // <4> - WITH [movie IN moviesList[batchStart .. batchStart + batchSize] | movie.title || ': ' || movie.plot] AS resources // <5> + WITH [movie IN moviesList[batchStart .. batchStart + batchSize] | movie.title || ': ' || movie.plot] AS batch // <5> CALL genai.vector.encodeBatch(batch, 'OpenAI', { token: $token }) YIELD index, vector CALL db.create.setNodeVectorProperty(moviesList[batchStart + index], 'embedding', vector) // <6> -} IN TRANSACTIONS OF 1 ROW <7> +} IN CONCURRENT TRANSACTIONS OF 1 ROW <7> ---- <1> xref:functions/aggregating.adoc#functions-collect[Collect] all returned `Movie` nodes into a `LIST`. @@ -196,12 +196,14 @@ CALL (moviesList, batchStart, batchSize) { // <4> Because vector embeddings can be very large, a larger batch size may require significantly more memory on the Neo4j server. Too large a batch size may also exceed the provider's threshold. <3> Process `Movie` nodes in increments of `batchSize`. +The end range `total-1` is due to `range` being inclusive on both ends. <4> A xref:subqueries/subqueries-in-transactions.adoc[`CALL` subquery] executes a separate transaction for each batch. Note that this `CALL` subquery uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables. If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. -<5> `resources` is a list of strings, each being the concatenation of `title` and `plot` of one movie. +<5> `batch` is a list of strings, each being the concatenation of `title` and `plot` of one movie. <6> The procedure sets `vector` as value for the property named `embedding` for the node at position `batchStart + index` in the `moviesList`. <7> Set to `1` the amount of batches to be processed at once. +Concurrency in transactions was introduced in Cypher 5.21 (see xref:subqueries/subqueries-in-transactions.adoc#concurrent-transactions[`CALL` subqueries -> Concurrent transactions]). [NOTE] This example may not scale to larger datasets, as `collect(m)` requires the whole result set to be loaded in memory. diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/index-hints.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/index-hints.adoc index 75540eccf..93d9cec90 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/index-hints.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/index-hints.adoc @@ -72,7 +72,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -173,7 +173,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -226,7 +226,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -267,7 +267,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -318,7 +318,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -362,7 +362,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -419,7 +419,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -477,7 +477,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -534,7 +534,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -587,7 +587,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -645,7 +645,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -704,7 +704,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -741,7 +741,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 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 9af1388a6..3343bb9e3 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc @@ -33,16 +33,16 @@ If `IF NOT EXISTS` is appended to the command, no error is thrown and nothing ha It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. As of Neo4j 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].] - -However, not all indexes have available configuration settings or more than one provider. +Index configuration settings can be specified using the `OPTIONS` clause. +However, not all indexes have available configuration settings. 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]. +Creating an index requires link:{neo4j-docs-base-uri}/operations-manual/current/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. +[NOTE] +An index cannot be used while its `state` is `POPULATING`, which occurs immediately after it is created. +To check the `state` of an index -- whether it is `ONLINE` (usable) or `POPULATING` (still being built; the `populationPercent` column shows the progress of the index creation) -- run the following command: `SHOW INDEXES`. [[create-range-index]] === Create a range index @@ -50,7 +50,7 @@ A newly created index is not immediately available but is created in the backgro 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 have no supported index configuration. [[range-indexes-supported-predicates]] [discrete] @@ -216,7 +216,7 @@ As of Neo4j 5.17, an informational notification is instead returned. 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 have no supported index configuration and, as of Neo4j 5.1, they have two index providers available, `text-2.0` (default -- see xref:indexes/search-performance-indexes/managing-indexes.adoc#text-indexes-trigram-indexes[Trigram indexing] below for more information) and `text-1.0` (deprecated). [[text-indexes-supported-predicates]] [discrete] @@ -235,7 +235,7 @@ However, other predicates are only used when it is known that the property is co * `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. +This means that a text index is not able to solve, for example, e.g. `a.prop = b.prop`, unless a xref:constraints/managing-constraints.adoc#create-property-type-constraints[property type constraint] also exists on the property. Text indexes support the following predicates: @@ -280,12 +280,24 @@ 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. +As of Neo4j 5.11, the above set of predicates can be extended with the use of property type constraints. +See the section about xref:indexes/search-performance-indexes/using-indexes.adoc#type-constraints[index compatibility and property 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. +[[text-indexes-trigram-indexes]] +==== Trigram indexing + +The default text index provider, `text-2.0`, uses trigram indexing. +This means that `STRING` values are indexed into overlapping trigrams, each containing three Unicode code points. +For example, the word `"developer"` would be indexed by the following trigrams: `["dev", "eve", "vel", "elo", "lop", "ope", "per"]`. + +This makes text indexes particularly suitable for substring (`CONTAINS`) and suffix (`ENDS WITH`) searches, as well as prefix searches (`STARTS WITH`). +For example, searches like `CONTAINS "vel"` or `ENDS WITH "per"` can be efficiently performed by directly looking up the relevant trigrams in the index. +By comparison, range indexes, which indexes `STRING` values lexicographically (see xref:indexes/search-performance-indexes/using-indexes.adoc#range-index-backed-order-by[Range index-backed `ORDER BY`] for more information) and are therefore more suited for prefix searches, would need to scan through all indexed values to check if `"vel"` existed anywhere within the text. +For more information, see xref:indexes/search-performance-indexes/using-indexes.adoc#text-indexes[The impact of indexes on query performance -> Text indexes]. + [discrete] [[text-indexes-examples]] ==== Examples @@ -294,7 +306,6 @@ Text indexes are only used for exact query matches. To perform approximate match * 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]] @@ -366,30 +377,13 @@ As of Neo4j 5.17, an informational notification is instead returned. `TEXT INDEX node_text_index_nickname FOR (e:Person) ON (e.nickname)` already exists. ---- -[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]] === 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. -Point indexes have supported index configuration, but only one index provider available, `point-1.0`. +Point indexes have supported index configuration. [discrete] [[point-indexes-supported-predicates]] @@ -426,8 +420,8 @@ 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. +As of Neo4j 5.11, the above set of predicates can be extended with the use of property type constraints. +See xref:indexes/search-performance-indexes/using-indexes.adoc#index-compatibility-type-constraints[Index compatibility and property 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]. @@ -554,7 +548,7 @@ Only one node label and one relationship type lookup index can exist at the same 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. +Token lookup indexes have no supported index configuration. [discrete] [[lookup-index-supported-predicates]] @@ -794,7 +788,7 @@ There already exists a constraint called 'bookRecommendations'. 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]. +Listing indexes requires link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-index[the `SHOW INDEX` privilege]. [discrete] [[listing-indexes-examples]] @@ -844,7 +838,6 @@ SHOW INDEXES | 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 @@ -871,25 +864,24 @@ 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'}" | -+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 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`)" | +| "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]}}" | +| "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`)" | +| "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]}}" | +| "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]}}" | +| "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`)" | +| "uniqueBookIsbn" | "RANGE" | "range-1.0" | {} | "CREATE CONSTRAINT `uniqueBookIsbn` FOR (n:`Book`) REQUIRE (n.`isbn`) IS UNIQUE" | ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ---- Note that `YIELD` is mandatory if the `RETURN` clause is used. @@ -1041,7 +1033,7 @@ With `IF EXISTS`, no error is thrown and nothing happens should the index not ex As of Neo4j 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]. +Dropping an index requires link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. [discrete] [[drop-indexes-examples]] @@ -1109,7 +1101,7 @@ 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]. +For more information, see xref:constraints/managing-constraints.adoc#drop-constraint[Drop a constraint by name]. [discrete] [[drop-a-non-existing-index]] 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 bcf88382b..7f0268162 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -28,7 +28,7 @@ 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. +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/current/backup-restore/restore-dump/[on-prem] instances. [[token-lookup-indexes]] == Token lookup indexes @@ -232,7 +232,7 @@ 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. +Text indexes do not store `STRING` properties alphabetically, and are instead optimized for suffix and contains searches (for more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#text-indexes-trigram-indexes[Create a text index -> Trigram indexing]). 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. @@ -892,15 +892,15 @@ The xref:functions/string.adoc#functions-tostring[`toString`] function can also [role=label--new-5.11] [[type-constraints]] -=== Type constraints +=== Property type constraints 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. +Since xref:constraints/managing-constraints.adoc#create-property-type-constraints[property 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. +To show this, the following example will first drop the existing range index on the `name` property (this is necessary because property 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 property type constraint, and compare the resulting execution plans. .Drop range index [source,cypher] @@ -938,7 +938,7 @@ 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. 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. +However, if a property 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] @@ -947,7 +947,7 @@ CREATE CONSTRAINT type_constraint FOR (n:PointOfInterest) REQUIRE n.name IS :: STRING ---- -.Rerun the query after the creation of a type constraint +.Rerun the query after the creation of a property type constraint [source,cypher] ---- PROFILE @@ -972,11 +972,11 @@ RETURN count(n) AS nodes 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 property 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. +Point indexes can be extended in the same way if a property 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. +Note that xref:constraints/managing-constraints.adoc#create-property-existence-constraints[property existence constraints] do not currently leverage index use in the same way. [[Heuristics]] == Heuristics: deciding what to index 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 647bb9a65..475aa6308 100644 --- a/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc @@ -3,7 +3,7 @@ = 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. +Unlike xref:indexes/search-performance-indexes/managing-indexes.adoc#create-range-index[range] and xref:indexes/search-performance-indexes/managing-indexes.adoc#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. @@ -42,7 +42,7 @@ As of Neo4j 5.17, an informational notification is instead returned showing the 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]. +Creating a full-text index requires the link:{neo4j-docs-base-uri}/operations-manual/current/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. @@ -83,18 +83,18 @@ The default analyzer (`standard-no-stop-words`) analyzes both the indexed values 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. +For example, the following stop words are included in Lucene’s `english` analyzer: "a", "an", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", and "with". 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. +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. +The link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_listavailableanalyzers[`db.index.fulltext.listAvailableAnalyzers()`] procedure shows all available analyzers. 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]. +For more information, see the link:{neo4j-docs-base-uri}/java-reference/current/extending-neo4j/full-text-analyzer-provider[Java Reference Manual -> Full-text index analyzer providers]. [[configuration-settings]] === Configuration settings @@ -129,18 +129,17 @@ The possible values for the `fulltext.analyzer` setting can be listed with the ` <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]. +For more information on how to configure full-text indexes, refer to the link:{neo4j-docs-base-uri}/operations-manual/current/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. +Unlike 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 query a full-text index, use either the link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_querynodes[`db.index.fulltext.queryNodes`] or the link:{neo4j-docs-base-uri}/operations-manual/current/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. -==== +An index cannot be used while its `state` is `POPULATING`, which occurs immediately after it is created. +To check the `state` of a full-text index -- whether it is `ONLINE` (usable) or `POPULATING` (still being built; the `populationPercent` column shows the progress of the index creation) -- run the following command: `SHOW FULLTEXT INDEXES`. This query uses the `db.index.fulltext.queryNodes` to look for `nils` in the previously created full-text index `namesAndTeams`: @@ -327,13 +326,13 @@ SHOW FULLTEXT INDEXES YIELD * .Result ---- -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | trackedSince | options | failureMessage | createStatement | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 4 | "communications" | "ONLINE" | 100.0 | "FULLTEXT" | "RELATIONSHIP" | ["REVIEWED", "EMAILED"] | ["message"] | "fulltext-1.0" | NULL | NULL | 0 | 2023-11-01T09:27:57.024Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}, indexProvider: "fulltext-1.0"} | "" | "CREATE FULLTEXT INDEX `communications` FOR ()-[r:`REVIEWED`|`EMAILED`]-() ON EACH [r.`message`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'standard-no-stop-words',`fulltext.eventually_consistent`: false}, indexProvider: 'fulltext-1.0'}" | -| 5 | "namesAndTeams" | "ONLINE" | 100.0 | "FULLTEXT" | "NODE" | ["Employee", "Manager"] | ["name", "team"] | "fulltext-1.0" | NULL | NULL | 0 | 2023-11-01T12:24:48.002Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}, indexProvider: "fulltext-1.0"} | "" | "CREATE FULLTEXT INDEX `namesAndTeams` FOR (n:`Employee`|`Manager`) ON EACH [n.`name`, n.`team`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'standard-no-stop-words',`fulltext.eventually_consistent`: false}, indexProvider: 'fulltext-1.0'}" | -| 6 | "peerReviews" | "ONLINE" | 100.0 | "FULLTEXT" | "NODE" | ["Employee", "Manager"] | ["peerReviews"] | "fulltext-1.0" | NULL | NULL | 0 | 2023-11-01T12:25:41.495Z | {indexConfig: {`fulltext.analyzer`: "english", `fulltext.eventually_consistent`: TRUE}, indexProvider: "fulltext-1.0"} | "" | "CREATE FULLTEXT INDEX `peerReviews` FOR (n:`Employee`|`Manager`) ON EACH [n.`peerReviews`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'english',`fulltext.eventually_consistent`: true}, indexProvider: 'fulltext-1.0'}" | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | trackedSince | options | failureMessage | createStatement | ++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 4 | "communications" | "ONLINE" | 100.0 | "FULLTEXT" | "RELATIONSHIP" | ["REVIEWED", "EMAILED"] | ["message"] | "fulltext-1.0" | NULL | NULL | 0 | 2023-11-01T09:27:57.024Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}} | "" | "CREATE FULLTEXT INDEX `communications` FOR ()-[r:`REVIEWED`|`EMAILED`]-() ON EACH [r.`message`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'standard-no-stop-words',`fulltext.eventually_consistent`: false}}" | +| 5 | "namesAndTeams" | "ONLINE" | 100.0 | "FULLTEXT" | "NODE" | ["Employee", "Manager"] | ["name", "team"] | "fulltext-1.0" | NULL | NULL | 0 | 2023-11-01T12:24:48.002Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}} | "" | "CREATE FULLTEXT INDEX `namesAndTeams` FOR (n:`Employee`|`Manager`) ON EACH [n.`name`, n.`team`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'standard-no-stop-words',`fulltext.eventually_consistent`: false}}" | +| 6 | "peerReviews" | "ONLINE" | 100.0 | "FULLTEXT" | "NODE" | ["Employee", "Manager"] | ["peerReviews"] | "fulltext-1.0" | NULL | NULL | 0 | 2023-11-01T12:25:41.495Z | {indexConfig: {`fulltext.analyzer`: "english", `fulltext.eventually_consistent`: TRUE}} | "" | "CREATE FULLTEXT INDEX `peerReviews` FOR (n:`Employee`|`Manager`) ON EACH [n.`peerReviews`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'english',`fulltext.eventually_consistent`: true}}" | ++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ---- For a full description of all return columns, see xref:indexes/search-performance-indexes/managing-indexes.adoc#listing-indexes-result-columns[Search-performance indexes -> Result columns for listing indexes]. @@ -392,7 +391,7 @@ The procedures for full-text indexes are listed in the table below: * 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. +* Full-text indexes can be checked by the link:{neo4j-docs-base-uri}/operations-manual/current/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. diff --git a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc index 13c8334b2..0f60dca75 100644 --- a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc @@ -30,7 +30,7 @@ image::vector_index_graph.svg[width="600",role="middle"] The graph contains 28863 nodes and 332522 relationships. To recreate the graph, download and import this link:https://github.com/neo4j-graph-examples/recommendations/blob/main/data/recommendations-embeddings-50.dump[dump file] to an empty Neo4j database (running version 5.13 or later). -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. +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/current/backup-restore/restore-dump/[on-prem] instances. [NOTE] The dump file used to load the dataset contains embeddings generated by https://openai.com/[OpenAI], using the model `text-embedding-ada-002`. @@ -71,7 +71,7 @@ A vector index allows you to retrieve a neighborhood of nodes or relationships b A vector index is a single-label, single-property index for nodes or a single-relationship-type, single-property index for relationships. It can be used to index nodes or relationships by `LIST` properties valid to the dimensions and vector similarity function of the index. -Note that the available vector index providers (`vector-2.0` (default) and `vector-1.0`) support different index schemas, property value types, and vector dimensions. +Note that the available vector index providers (`vector-2.0` (default) and `vector-1.0` (deprecated)) support different index schemas, property value types, and vector dimensions. For more information, see xref:indexes/semantic-indexes/vector-indexes.adoc#vector-index-providers[]. A vector index is created by using the `CREATE VECTOR INDEX` command. @@ -84,7 +84,7 @@ The index name must be unique among both indexes and constraints. + A newly created index is not immediately available but is created in the background. [TIP] -Creating indexes requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `CREATE INDEX` privilege]. +Creating indexes requires link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-index[the `CREATE INDEX` privilege]. .Create vector index for `Movie` nodes on the `embedding` property label:new[Introduced in 5.15] [source, cypher] @@ -110,7 +110,7 @@ In this example, the vector dimension is explicitly set to `1536` and the vector To read more about the available similarity functions, see xref:indexes/semantic-indexes/vector-indexes.adoc#similarity-functions[]. [NOTE] -Prior to Neo4j 5.15, node vector indexes were created using the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_createnodeindex[`db.index.vector.createNodeIndex`] procedure. +Prior to Neo4j 5.15, node vector indexes were created using the link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_createnodeindex[`db.index.vector.createNodeIndex`] procedure. You can also create a vector index for relationships with a particular type on a given property using the following syntax: @@ -194,7 +194,11 @@ Default value::: `100` [[query-vector-index]] == Query vector indexes -To query a node vector index, use the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] procedure. +To query a node vector index, use the link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] procedure. + +[NOTE] +An index cannot be used while its `state` is `POPULATING`, which occurs immediately after it is created. +To check the `state` of a vector index -- whether it is `ONLINE` (usable) or `POPULATING` (still being built; the `populationPercent` column shows the progress of the index creation) -- run the following command: `SHOW VECTOR INDEXES`. .Signature for `db.index.vector.queryNodes` [source,syntax] @@ -236,7 +240,7 @@ Note that all movies returned have a plot centred around criminal family organiz The `score` results are returned in _descending order_, where the best matching result entry is put first (in this case, `The Godfather` has a similarity score of `1.0`, which is to be expected as the index was queried with this specific property). If the query vector itself is not wanted, adding the predicate `WHERE score < 1` removes identical vectors. -To query a relationship vector index, use the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_queryRelationships[`db.index.vector.queryRelationships`] procedure. +To query a relationship vector index, use the link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_queryRelationships[`db.index.vector.queryRelationships`] procedure. .Signature for `db.index.vector.queryRelationships` label:new[Introduced in 5.18] [source,syntax] @@ -253,12 +257,12 @@ Use xref:functions/vector.adoc[] to compute the similarity score between two spe == Performance suggestions Vector indexes can take advantage of the incubated Java 20 Vector API for noticeable speed improvements. -If you are using a compatible version of Java, you can add the following setting to your link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings/#config_server.jvm.additional[configuration settings]: +If you are using a compatible version of Java, you can add the following setting to your link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings/#config_server.jvm.additional[configuration settings]: .Configuration settings [source,config] ---- -server.jvm.additional=--add-modules jdk.incubator.vector +server.jvm.additional=--add-modules=jdk.incubator.vector ---- [[show-vector-indexes]] @@ -268,7 +272,7 @@ To list all vector indexes in a database, use the `SHOW VECTOR INDEXES` command. This is the same xref:indexes/search-performance-indexes/managing-indexes.adoc#list-indexes[`SHOW` command as for other indexes], with the index type filtering on `VECTOR`. [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]. +Listing indexes requires link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-index[the `SHOW INDEX` privilege]. .Show all vector indexes ==== @@ -306,11 +310,11 @@ SHOW VECTOR INDEXES YIELD * .Result [source, role=queryresult] ---- -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint| lastRead | readCount | trackedSince | options | failureMessage | createStatement | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 2 | "moviePlots"| "ONLINE" | 100.0 | "VECTOR" | "NODE" | ["Movie"] | ["embedding"] | "vector-2.0" | NULL | 2024-05-07T09:19:09.225Z | 47 | 2024-05-07T08:26:19.072Z | {indexConfig: {indexConfig: {`vector.dimensions`: 1536, `vector.hnsw.m`: 16, `vector.quantization.enabled`: TRUE, `vector.similarity_function`: "COSINE", `vector.hnsw.ef_construction`: 100}, indexProvider: "vector-2.0"}, indexProvider: "vector-2.0"} | "" | "CREATE VECTOR INDEX `moviePlots` FOR (n:`Movie`) ON (n.`embedding`) OPTIONS {indexConfig: {`vector.dimensions`: 1536,`vector.hnsw.ef_construction`: 100,`vector.hnsw.m`: 16,`vector.quantization.enabled`: true,`vector.similarity_function`: 'COSINE'}, indexProvider: 'vector-2.0'}" | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint| lastRead | readCount | trackedSince | options | failureMessage | createStatement | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2 | "moviePlots"| "ONLINE" | 100.0 | "VECTOR" | "NODE" | ["Movie"] | ["embedding"] | "vector-2.0" | NULL | 2024-05-07T09:19:09.225Z | 47 | 2024-05-07T08:26:19.072Z | {indexConfig: {`vector.dimensions`: 1536, `vector.hnsw.m`: 16, `vector.quantization.enabled`: TRUE, `vector.similarity_function`: "COSINE", `vector.hnsw.ef_construction`: 100}, indexProvider: "vector-2.0"} | "" | "CREATE VECTOR INDEX `moviePlots` FOR (n:`Movie`) ON (n.`embedding`) OPTIONS {indexConfig: {`vector.dimensions`: 1536,`vector.hnsw.ef_construction`: 100,`vector.hnsw.m`: 16,`vector.quantization.enabled`: true,`vector.similarity_function`: 'COSINE'}}" | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ---- To return only specific details, specify the desired column name(s) after the `YIELD` clause. @@ -339,7 +343,7 @@ A vector index is dropped by using the xref:indexes/search-performance-indexes/m As of Neo4j 5.16, the index name can also be given as a parameter when dropping an index: `DROP INDEX $name`. [TIP] -Dropping indexes 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]. +Dropping indexes requires link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. .Drop a vector index [source, cypher] @@ -350,9 +354,11 @@ DROP INDEX moviePlots [[vector-index-providers]] == Vector index providers for compatibility -As of Neo4j 5.18, the default and preferred vector index provider is `vector-2.0`. +As of Neo4j 5.18, the default index provider is `vector-2.0`. +As of Neo4j 5.26, it is no longer possible to specify an index provider when creating indexes. +Instead, Neo4j selects the most performant provider (currently `vector-2.0`). + Previously created `vector-1.0` indexes will continue to function. -New indexes can still be created with the `vector-1.0` provider if it is specified in the `OPTIONS` map. .Learn more about vector index provider differences [%collapsible] @@ -472,30 +478,30 @@ image::euclidean_similarity_equation.svg["The Euclidean of vector v and vector u | Usage | Procedure | Description | Create node vector index. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_createnodeindex[`db.index.vector.createNodeIndex`] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_createnodeindex[`db.index.vector.createNodeIndex`] | Create a vector index for the specified label and property with the given vector dimension using the given similarity function. Replaced by the `CREATE VECTOR INDEX` command. | Use node vector index. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] | Query the given node vector index. Returns the requested number of approximate nearest neighbor nodes and their similarity score, ordered by score. | Use relationship vector index. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_queryRelationships[`db.index.vector.queryRelationships`] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_queryRelationships[`db.index.vector.queryRelationships`] | Query the given relationship vector index. Returns the requested number of approximate nearest neighbor relationships and their similarity score, ordered by score. label:new[Introduced in 5.18] | Set node vector property. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`] -| Update a given node property with the given vector in a more space-efficient way than directly using xref:clauses/set.adoc#set-set-a-property[`SET`]. Replaces link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_create_setVectorProperty[`db.create.setVectorProperty`]. label:beta[] label:new[Introduced in 5.13] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`] +| Update a given node property with the given vector in a more space-efficient way than directly using xref:clauses/set.adoc#set-set-a-property[`SET`]. Replaces link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_create_setVectorProperty[`db.create.setVectorProperty`]. label:beta[] label:new[Introduced in 5.13] | Set node vector property. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_create_setVectorProperty[`db.create.setVectorProperty`] -| Replaced by link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`]. label:deprecated[] label:beta[] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_create_setVectorProperty[`db.create.setVectorProperty`] +| Replaced by link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`]. label:deprecated[] label:beta[] | Set relationship vector property. -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_create_setRelationshipVectorProperty[`db.create.setRelationshipVectorProperty`] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_create_setRelationshipVectorProperty[`db.create.setRelationshipVectorProperty`] | Update a given relationship property with the given vector in a more space-efficient way than directly using xref:clauses/set.adoc#set-set-a-property[`SET`]. label:beta[] label:new[Introduced in 5.18] |=== @@ -529,7 +535,7 @@ The following table lists the known issues and, if fixed, the version in which t |=== | Known issues | Fixed in -| The creation of a vector index using the legacy procedure link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_createnodeindex[`db.index.vector.createNodeIndex`] may fail with an error in Neo4j 5.18 and later if the database was last written to with a version prior to Neo4j 5.11, and the legacy procedure is the first write operation used on the newer version. +| The creation of a vector index using the legacy procedure link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_createnodeindex[`db.index.vector.createNodeIndex`] may fail with an error in Neo4j 5.18 and later if the database was last written to with a version prior to Neo4j 5.11, and the legacy procedure is the first write operation used on the newer version. In Neo4j 5.20, the error was clarified. [TIP] -- @@ -588,7 +594,7 @@ This is caused by an issue in the handling of index capabilities on followers. -- Because index capabilities will be correctly configured on a restart, this issue can be worked around by rolling the cluster after vector index creation. -For more information about clustering in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering[Operations Manual -> Clustering]. +For more information about clustering in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/current/clustering[Operations Manual -> Clustering]. -- | Neo4j 5.14 @@ -605,9 +611,9 @@ To work around this issue if you need to run multiple vector index queries and m | xref:clauses/listing-procedures.adoc[`SHOW PROCEDURES`] does not show the vector index procedures: -* link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_create_setVectorProperty[`db.create.setVectorProperty`] -* link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_createNodeIndex[`db.index.vector.createNodeIndex`] -* link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] +* link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_create_setVectorProperty[`db.create.setVectorProperty`] +* link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_createNodeIndex[`db.index.vector.createNodeIndex`] +* link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] [NOTE] -- @@ -630,7 +636,7 @@ d| The validation for xref:indexes/semantic-indexes/vector-indexes.adoc#similari This can lead to certain large component vectors being incorrectly indexed, and return a similarity score of `±0.0`. | Neo4j 5.12 -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] query vector validation is incorrect with a xref:indexes/semantic-indexes/vector-indexes.adoc#similarity-functions[cosine] vector index. The {l2-norm} validation only considers the last component of the vector. If that component is `±0.0`, an otherwise valid query vector will be thrown as invalid. This can also result in some invalid vectors being used to query, and return a similarity score of `±0.0`. +| link:{neo4j-docs-base-uri}/operations-manual/current/#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] query vector validation is incorrect with a xref:indexes/semantic-indexes/vector-indexes.adoc#similarity-functions[cosine] vector index. The {l2-norm} validation only considers the last component of the vector. If that component is `±0.0`, an otherwise valid query vector will be thrown as invalid. This can also result in some invalid vectors being used to query, and return a similarity score of `±0.0`. [TIP] -- @@ -642,7 +648,7 @@ It is _recommended_ to normalize your vectors (if needed), and use a xref:indexe | 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:{neo4j-docs-base-uri}/operations-manual/{page-version}/backup-restore/copy-database/[Copying a database store] with a vector index does not log the recreation command, and instead logs an error: +| link:{neo4j-docs-base-uri}/operations-manual/current/backup-restore/copy-database/[Copying a database store] with a vector index does not log the recreation command, and instead logs an error: ---- ERROR: [StoreCopy] Unable to format statement for index 'index-name' ---- diff --git a/modules/ROOT/pages/indexes/syntax.adoc b/modules/ROOT/pages/indexes/syntax.adoc index baede5cba..e1536f2a9 100644 --- a/modules/ROOT/pages/indexes/syntax.adoc +++ b/modules/ROOT/pages/indexes/syntax.adoc @@ -4,7 +4,7 @@ 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]. +More details about the syntax can be found in the link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/syntax/[Operations Manual -> Cypher syntax for administration commands]. [[create-index]] == CREATE INDEX @@ -27,18 +27,18 @@ As of Neo4j 5.17, an informational notification is instead returned showing the 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. +Index configuration settings can be specified using the `OPTIONS` clause. +However, not all indexes have available configuration settings. 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]. +Creating an index requires link:{neo4j-docs-base-uri}/operations-manual/current/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. +Range indexes have no supported index configuration. +The `OPTIONS` map has, therefore, been omitted from the syntax below. .Create a range index for a node label, either on a single property or composite [source,syntax] @@ -67,13 +67,15 @@ For more information, see xref:indexes/search-performance-indexes/managing-index [[create-text-index]] === Text indexes +Text indexes have no supported index configuration. +The `OPTIONS` map has, therefore, been omitted from the syntax below. + .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 @@ -82,11 +84,8 @@ ON (n.propertyName_1) 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. @@ -113,7 +112,6 @@ 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` @@ -133,8 +131,8 @@ For more information, see xref:indexes/search-performance-indexes/managing-index [[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. +Token lookup indexes have no supported index configuration. +The `OPTIONS` map has, therefore, been omitted from the syntax below. .Create a node label lookup index [source,syntax] @@ -177,7 +175,6 @@ 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). @@ -207,9 +204,6 @@ ON (r.propertyName) [OPTIONS “{“ option: value[, …] “}”] ---- -As of Neo4j 5.18, vector indexes have two vector index providers available, `vector-2.0` (default) and `vector-1.0`. -For more information, see xref:indexes/semantic-indexes/vector-indexes.adoc#vector-index-providers[Vector index providers for compatibility]. - For a full list of all vector index settings, see xref:indexes/semantic-indexes/vector-indexes.adoc#configuration-settings[Vector index configuration settings]. Note that the `OPTIONS` clause was mandatory prior to Neo4j 5.23 because it was necessary to configure the `vector.dimensions` and `vector.similarity_function` settings when creating a vector index. @@ -232,7 +226,7 @@ For more information, see xref:indexes/semantic-indexes/vector-indexes.adoc#crea == 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]. +Listing indexes requires link:{neo4j-docs-base-uri}/operations-manual/current/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] @@ -310,7 +304,7 @@ With `IF EXISTS`, no error is thrown and nothing happens should the index not ex As of Neo4j 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]. +Dropping indexes requires link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. .Drop an index of any index type [source,syntax] diff --git a/modules/ROOT/pages/introduction/cypher-aura.adoc b/modules/ROOT/pages/introduction/cypher-aura.adoc index c1cd41633..380ea527f 100644 --- a/modules/ROOT/pages/introduction/cypher-aura.adoc +++ b/modules/ROOT/pages/introduction/cypher-aura.adoc @@ -2,7 +2,7 @@ = Cypher and Aura :description: This section provides an introduction to the Cypher query language. -This section gives a brief overview of Aura, and how Cypher differs to users of Aura. +This page provides a brief overview of Neo4j Aura and its relationship to Cypher. == What is Aura? @@ -13,41 +13,35 @@ AuraDB is a graph database service for developers building intelligent applicati AuraDB is available on the following tiers: * AuraDB Free -* AuraDB Pro -* AuraDB Enterprise +* AuraDB Professional +* AuraDB Business Critical +* AuraDB Virtual Dedicated Cloud 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 +* Graph Data Science Community +* Graph Data Science Enterprise +* AuraDS Professional * 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. +Most Cypher features are available across all tiers of Aura. +However, certain features are not supported in 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[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. -|=== +Additionally, some Cypher features are exclusive to AuraDB Business Critical and AuraDB Virtual Dedicated Cloud tiers. +These primarily fall under database administration and role-based access control capabilities. +For more information, see the link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[Operations Manual -> Database administration] and the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/[Operations Manual -> Authentication and authorization]. == 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 Cypher Cheat Sheet can be accessed link:{neo4j-docs-base-uri}/cypher-cheat-sheet/{page-version}/auradb-enterprise/[here]. +The Cypher Cheat Sheet can be accessed link:{neo4j-docs-base-uri}/cypher-cheat-sheet/current/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. +Note that the default tier is AuraDB Virtual Dedicated Cloud. diff --git a/modules/ROOT/pages/introduction/cypher-neo4j.adoc b/modules/ROOT/pages/introduction/cypher-neo4j.adoc index 11c70a69e..6773e48ed 100644 --- a/modules/ROOT/pages/introduction/cypher-neo4j.adoc +++ b/modules/ROOT/pages/introduction/cypher-neo4j.adoc @@ -16,7 +16,7 @@ Cypher works almost identically between the two editions, but there are key area |=== | Feature | Enterprise Edition | Community Edition -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/[Multi-database] +| link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[Multi-database] a| Any number of user databases. a| @@ -24,24 +24,20 @@ Only `system` and one user database. | Role-based security a| -User, role, and privilege management for flexible link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/manage-privileges/[access control] and link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/manage-privileges/[sub-graph access control]. +User, role, and privilege management for flexible link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/manage-privileges/[access control] and link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/manage-privileges/[sub-graph access control]. a| -link:{neo4j-docs-base-uri}/operations-manual/{page-version}authentication-authorization/manage-users[Multi-user management]. +link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/manage-users[Multi-user management]. All users have full access rights. | Constraints a| All constraints: -xref::constraints/examples.adoc#constraints-examples-node-property-existence[node property existence constraints], -xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship property existence constraints], -xref::constraints/examples.adoc#constraints-examples-node-property-type[node property type constraints], -xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship property type 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]. +xref::constraints/managing-constraints.adoc#create-property-existence-constraints[node and relationship property existence constraints], +xref::constraints/managing-constraints.adoc#create-property-type-constraints[node and relationship property type constraints], +xref::constraints/managing-constraints.adoc#create-property-uniqueness-constraints[node and relationship property uniqueness constraints], +xref::constraints/managing-constraints.adoc#create-key-constraints[node and 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/managing-constraints.adoc#create-property-uniqueness-constraints[node and relationship property uniqueness constraints]. |=== @@ -59,7 +55,7 @@ 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]. +For more information, see link:{neo4j-docs-base-uri}/operations-manual/current/composite-databases/[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. @@ -74,12 +70,12 @@ 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 link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/[Database management] and link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/[Access control]. +For more information about the _system_ database, see the sections on link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/[Database management] and link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/[Access control]. === Query considerations Most of the time Cypher queries are reading or updating queries, which are run against a graph. -There are also link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/syntax[administrative commands] that apply to a database, or to the entire DBMS. +There are also link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/syntax[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. @@ -96,7 +92,7 @@ In short, an updating query always either fully succeeds or does not succeed at [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]. +For memory configuration in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/current/performance/memory-configuration[Neo4j Operations Manual -> Memory configuration]. ==== === Explicit and implicit transactions @@ -123,9 +119,9 @@ Explicit transactions cannot be managed directly from queries, they must be mana 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]. +* For information on using transactions over the HTTP API, see the link:{neo4j-docs-base-uri}/http-api/current/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/current/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/current/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/modules/ROOT/pages/introduction/cypher-overview.adoc b/modules/ROOT/pages/introduction/cypher-overview.adoc index a8d7ea846..865cde7bf 100644 --- a/modules/ROOT/pages/introduction/cypher-overview.adoc +++ b/modules/ROOT/pages/introduction/cypher-overview.adoc @@ -36,7 +36,7 @@ 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]). +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/managing-constraints.adoc#create-property-existence-constraints[property 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*:: @@ -82,4 +82,4 @@ RETURN actor.name 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/current[APOC Core page]. diff --git a/modules/ROOT/pages/introduction/index.adoc b/modules/ROOT/pages/introduction/index.adoc index 3026c693d..01fef1f03 100644 --- a/modules/ROOT/pages/introduction/index.adoc +++ b/modules/ROOT/pages/introduction/index.adoc @@ -13,10 +13,10 @@ Cypher is Neo4j’s declarative query language, allowing users to unlock the ful 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. -If you are new to Cypher and Neo4j, you can visit the link:{neo4j-docs-base-uri}/getting-started/{page-version}/cypher-intro/[Getting Started Guide -> Introduction to Cypher] chapter. +If you are new to Cypher and Neo4j, you can visit the link:{neo4j-docs-base-uri}/getting-started/current/cypher-intro/[Getting Started Guide -> Introduction to Cypher] chapter. Additionally, https://graphacademy.neo4j.com/[Neo4j GraphAcademy] has a variety of free courses tailored for all levels of experience. -For a reference of all available Cypher features, see the link:{neo4j-docs-base-uri}/cypher-cheat-sheet/{page-version}/[Cypher Cheat Sheet]. +For a reference of all available Cypher features, see the link:{neo4j-docs-base-uri}/cypher-cheat-sheet/current/[Cypher Cheat Sheet]. For a downloadable PDF version of the Cypher Manual, visit the link:{neo4j-docs-base-uri}/resources/docs-archive/#_cypher_query_language[Neo4j documentation archive]. diff --git a/modules/ROOT/pages/patterns/fixed-length-patterns.adoc b/modules/ROOT/pages/patterns/fixed-length-patterns.adoc index 96e66637c..c6ceb9ad1 100644 --- a/modules/ROOT/pages/patterns/fixed-length-patterns.adoc +++ b/modules/ROOT/pages/patterns/fixed-length-patterns.adoc @@ -1,7 +1,7 @@ :description: Information about node, relationship, and path patterns. -= Fixed length patterns += Fixed-length patterns -The most basic form of graph pattern matching in Cypher involves the matching of fixed length patterns. +The most basic form of graph pattern matching in Cypher involves the matching of fixed-length patterns. This includes node patterns, relationship patterns, and path patterns. [[node-patterns]] @@ -57,10 +57,14 @@ MATCH (n { mode: 'Rail' }) More general predicates can be expressed with a `WHERE` clause. The following matches nodes whose name property starts with `Preston`: -[source, role=noheader] +// tag::patterns_fixed_length_patterns_node_pattern[] +[source, cypher] ---- MATCH (n:Station WHERE n.name STARTS WITH 'Preston') +RETURN n ---- +// end::patterns_fixed_length_patterns_node_pattern[] + See the xref:patterns/reference.adoc#node-patterns[node patterns] reference section for more details. @@ -119,7 +123,7 @@ See the xref:patterns/reference.adoc#relationship-patterns[relationship patterns == Path patterns Any valid path starts and ends with a node, with relationships between each node (if there is more than one node). -Fixed length path patterns have the same restrictions, and for all valid path patterns the following are true: +Fixed-length path patterns have the same restrictions, and for all valid path patterns the following are true: * They have at least one node pattern. * They begin and end with a node pattern. @@ -204,11 +208,13 @@ In order to return the name of each `Stop` that calls at a `Station`, declare a The results will then have a row containing the departs value of each `Stop` for each match shown above: .Query +// tag::patterns_fixed_length_patterns_path_pattern[] [source, cypher] ---- MATCH (s:Stop)-[:CALLS_AT]->(:Station {name: 'Denmark Hill'}) RETURN s.departs AS departureTime ---- +// end::patterns_fixed_length_patterns_path_pattern[] .Result [role="queryresult",options="header,footer",cols="1*(z:B WHERE z.h > 2)){1,5} RETURN [n in x | n.h] AS x_h, [n in z | n.h] AS z_h ---- -Compared to the fixed length quantifier `\{2}`, this also matches paths of length one and three, but no matches exist for length greater than three: +Compared to the fixed-length quantifier `\{2}`, this also matches paths of length one and three, but no matches exist for length greater than three: [options="header",cols="2*+(:B)) [[shortest-functions-rules-path-pattern-length]] ==== Path pattern length -There must be exactly one relationship pattern in the path pattern. +There must be exactly one relationship pattern in the path pattern, and the lower bound should be 0 or 1. .Allowed [source] @@ -1427,6 +1428,8 @@ shortestPath((a)-[:R*1..5]-(b)) ---- shortestPath((a)-[:R*1..5]-(b)-->(:X)) +shortestPath((a)-[:R*2..5]-(b)) + shortestPath((:A)) allShortestPaths((a:A)-[:S*]->(:B), (a)-[:R*1..3]->(:C)) @@ -1470,14 +1473,14 @@ Return a single shortest path for each distinct pair of nodes matching `(:A)` an [source, role=noheader] ---- -MATCH shortestPath((:A)-[:R]->{0,10}(:B)) +MATCH shortestPath((:A)-[:R*0..10]->(:B)) ---- Return all paths equal to the shortest path length for each distinct pair of nodes matching `(:A)` and `(:B)`: [source, role=noheader] ---- -MATCH allShortestPaths((:A)-[:R]->{0,10}(:B)) +MATCH allShortestPaths((:A)-[:R*0..10]->(:B)) ---- [[graph-patterns]] diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index 01e61f3ba..cc8e815c6 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -69,12 +69,14 @@ The paths matched by a xref:patterns/fixed-length-patterns.adoc#path-patterns[pa For example, the following example uses `SHORTEST 1` to return the length of the shortest path between `Worcester Shrub Hill` and `Bromsgrove`: .Query +// tag::patterns_shortest_paths_shortest_k[] [source, cypher] ---- MATCH p = SHORTEST 1 (wos:Station)-[:LINK]-+(bmv:Station) WHERE wos.name = "Worcester Shrub Hill" AND bmv.name = "Bromsgrove" RETURN length(p) AS result ---- +// end::patterns_shortest_paths_shortest_k[] [TIP] Note that this and the following examples in this section use a quantified relationship `-[:LINK]-+`, which is composed of a relationship pattern `-[:LINK]-` and a postfix quantifier `+`. The relationship pattern is only concerned with following relationships with type `LINK`, and will otherwise traverse any node along the way. There is no arrowhead `<` or `>` on the relationship pattern, allowing the pattern to match relationships going in either direction. This represents the fact that trains can go in both directions along the `LINK` relationships between Stations. The `+` quantifier means that one or more relationships should be matched. For more information, see xref:patterns/reference.adoc#quantified-relationships[Syntax and semantics - quantified relationships]. @@ -157,12 +159,14 @@ If there had been only four possible paths between the two Stations, then only t To return all paths that are tied for shortest length, use the keywords `ALL SHORTEST`: .Query +// tag::patterns_shortest_paths_all_shortest[] [source,cypher] ---- MATCH p = ALL SHORTEST (wos:Station)-[:LINK]-+(bmv:Station) WHERE wos.name = "Worcester Shrub Hill" AND bmv.name = "Bromsgrove" RETURN [n in nodes(p) | n.name] AS stops ---- +// end::patterns_shortest_paths_all_shortest[] .Result [role="queryresult",options="header,footer",cols="m"] @@ -184,12 +188,14 @@ To return all paths that are tied for first, second, and so on up to the kth sho For example, the following returns the first and second shortest length paths between `Worcester Shrub Hill` and `Bromsgrove`: .Query +// tag::patterns_shortest_paths_shortest_k_groups[] [source,cypher] ---- MATCH p = SHORTEST 2 GROUPS (wos:Station)-[:LINK]-+(bmv:Station) WHERE wos.name = "Worcester Shrub Hill" AND bmv.name = "Bromsgrove" RETURN [n in nodes(p) | n.name] AS stops, length(p) AS pathLength ---- +// end::patterns_shortest_paths_shortest_k_groups[] .Result [role="queryresult",options="header,footer",cols="2m,m"] @@ -240,12 +246,14 @@ It returns the same as `SHORTEST 1`, but by using the `ANY` keyword the intent o For example, the following query shows that there exists a route from `Pershore` to `Bromsgrove` where the distance between each pair of stations is less than 10 miles: .Query +// tag::patterns_shortest_paths_any[] [source,cypher] ---- MATCH path = ANY (:Station {name: 'Pershore'})-[l:LINK WHERE l.distance < 10]-+(b:Station {name: 'Bromsgrove'}) RETURN [r IN relationships(path) | r.distance] AS distances ---- +// end::patterns_shortest_paths_any[] .Result [role="queryresult",options="header,footer",cols="m"] @@ -595,4 +603,4 @@ RETURN p [TIP] Sometimes the planner cannot make reliable estimations about how many nodes a pattern node will match. -Consider using a xref:constraints/index.adoc#unique-node-property[uniqueness constraint] where applicable to help the planner get more reliable estimates. +Consider using a xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[property uniqueness constraint] where applicable to help the planner get more reliable estimates. diff --git a/modules/ROOT/pages/patterns/variable-length-patterns.adoc b/modules/ROOT/pages/patterns/variable-length-patterns.adoc index 89041e57a..04c401e7f 100644 --- a/modules/ROOT/pages/patterns/variable-length-patterns.adoc +++ b/modules/ROOT/pages/patterns/variable-length-patterns.adoc @@ -1,5 +1,5 @@ :description: Information about quantified path patterns, quantified relationships, and group variables. -= Variable length patterns += Variable-length patterns Cypher can be used to match patterns of a variable or an unknown length. Such patterns can be found using quantified path patterns and quantified relationships. @@ -172,6 +172,7 @@ Translating the union of fixed-length path patterns into a quantified path patte The following query adds a `RETURN` clause that yields the departure and arrival times of the two services: .Query +// tag::patterns_variable_length_patterns_qpp[] [source, cypher] ---- MATCH (:Station { name: 'Denmark Hill' })<-[:CALLS_AT]-(d:Stop) @@ -179,6 +180,8 @@ MATCH (:Station { name: 'Denmark Hill' })<-[:CALLS_AT]-(d:Stop) (a:Stop)-[:CALLS_AT]->(:Station { name: 'Clapham Junction' }) RETURN d.departs AS departureTime, a.arrives AS arrivalTime ---- +// end::patterns_variable_length_patterns_qpp[] + .Result [role="queryresult",options="header,footer",cols="2*` and not the node patterns abutting it. More generally, where a path pattern contained in a quantified path pattern has the following form: @@ -523,6 +529,7 @@ In this example, an inline predicate can be added that takes advantage of the ge To compose the predicate, the xref:functions/spatial.adoc#functions-distance[point.distance()] function is used to compare the distance between the left-hand `Station` (`a`) and the right-hand `Station` (`b`) for each node-pair along the path to the destination `North Dulwich`: .Query +// tag::patterns_variable_length_patterns_predicates_in_qpp[] [source,cypher] ---- MATCH (bfr:Station {name: "London Blackfriars"}), @@ -534,6 +541,8 @@ MATCH p = (bfr) RETURN reduce(acc = 0, r in relationships(p) | round(acc + r.distance, 2)) AS distance ---- +// end::patterns_variable_length_patterns_predicates_in_qpp[] + .Result [role="queryresult",options="header,footer",cols="m"] diff --git a/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc b/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc index 022232e42..ec6c0cb5c 100644 --- a/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc +++ b/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc @@ -71,7 +71,7 @@ image::patterns_qpp_solutions.svg[width="700",role="middle"] For the purposes of understanding Cypher execution plans, however, the query result is less interesting than the planning that produces it. -[[runtimes-reading-execution-plans]] +[[reading-execution-plans]] == Reading execution plans The Cypher planner produces logical plans which describe how a particular query is going to be executed. @@ -91,7 +91,7 @@ MATCH (:Station { name: 'Denmark Hill' })<-[:CALLS_AT]-(d:Stop) RETURN count(*) ---- -This is the resulting execution planfootnote:[The format of the execution plans displayed in this section are those generated when using link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/cypher-shell[Cypher Shell]. +This is the resulting execution planfootnote:[The format of the execution plans displayed in this section are those generated when using link:{neo4j-docs-base-uri}/operations-manual/current/tools/cypher-shell[Cypher Shell]. The execution plans generated by link:{neo4j-docs-base-uri}/browser-manual/current[Neo4j Browser] use a different format.]: [role="queryplan"] 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 4f9d332ed..720d0d721 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc @@ -2,11 +2,26 @@ :page-aliases: execution-plans/operators.adoc = Operators in detail -This page contains details and an example query for all operators available in Cypher. -The operators are grouped by the similarity of their characteristics. +Understanding the roles of different operators in an execution plan can be an important step in making queries more efficient. +This page contains details and examples for each of the operators used by the Cypher planner. +For an overview of how the Cypher planner uses operators, see xref:planning-and-tuning/execution-plans.adoc#reading-execution-plans[ Understanding execution plans -> Reading execution plans]. + +The operators are grouped into categories based on the role they fulfill in executing a Cypher query: + +* xref:planning-and-tuning/operators/operators-detail.adoc#leaf-operators[Leaf operators (scans and seeks)] +* xref:planning-and-tuning/operators/operators-detail.adoc#nested-loops-join-operators[Nested loops and join operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#traversal-operators[Traversal operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#union-operators[Union operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#aggregation-operators[Aggregation operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#filter-order-projection-operators[Filter, order, and projection operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#sort-limit-operators[Sort and limit operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#data-modification-operators[Data modification operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#schema-system-operators[Schema and system operators] + +[NOTE] +Certain operators are only used by particular xref:planning-and-tuning/runtimes/concepts.adoc[runtime]. +If so, the example queries will be prefixed with a runtime selection. -Certain operators are only used by a subset of the xref::planning-and-tuning/runtimes/concepts.adoc[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] @@ -81,9 +96,16 @@ CREATE CONSTRAINT constraint_WORKS_IN_badgeNumber_unique IF NOT EXISTS FOR ()-[r //// +[[leaf-operators]] +== Leaf operators (scans and seeks) + +Leaf operators are the initial operations in a query execution plan that directly access data. They are called "leaf" because they are at the outermost level of the query execution tree, with no operations below them. Once these operators retrieve the necessary data, the results are passed to subsequent operators that perform more complex operations. + +Seek operators target specific data items (indexed or otherwise) while scan operators iterate through the data (indexed or otherwise) to find matches. + + [[query-plan-all-nodes-scan]] -== All Nodes Scan -// AllNodesScan +=== All Nodes Scan The `AllNodesScan` operator reads all nodes from the node store. The variable that will contain the nodes is seen in the arguments. @@ -108,7 +130,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -127,9 +149,7 @@ Total database accesses: 36, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-all-nodes-scan]] -== Partitioned All Nodes Scan -// PartitionedAllNodesScan -// New in 5.17 +=== Partitioned All Nodes Scan 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. @@ -153,7 +173,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -171,30 +191,22 @@ Total database accesses: 106 ====== -[[query-plan-directed-relationship-index-scan]] -== Directed Relationship Index Scan -// DirectedRelationshipIndexScan - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX range_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) ----- -//// +[[query-plan-node-by-label-scan]] +=== Node By Label Scan +// NodeByLabelScan -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. +The `NodeByLabelScan` operator fetches all nodes with a specific label from the node label index. -.DirectedRelationshipIndexScan +.NodeByLabelScan ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title IS NOT NULL -RETURN r +MATCH (person:Person) +RETURN person ---- .Query Plan @@ -204,33 +216,32 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 15 | 15 | 0 | | | | | -| | +----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title IS NOT NULL | 15 | 15 | 16 | 120 | 3/1 | 2.464 | Fused in Pipeline 0 | -+--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | person | 14 | 14 | 0 | | | | | +| | +---------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | person:Person | 14 | 14 | 15 | 120 | 2/1 | 0.522 | Fused in Pipeline 0 | ++------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 16, total allocated memory: 184 +Total database accesses: 15, total allocated memory: 184 ---- ====== [role=label--new-5.17] -[[query-plan-partitioned-directed-relationship-index-scan]] -== Partitioned Directed Relationship Index Scan -// PartitionedDirectedRelationshipIndexScan -// New in 5.17 +[[query-plan-partitioned-node-by-label-scan]] +=== Partitioned Node By Label Scan -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]. +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. -.PartitionedDirectedRelationshipIndexScan + +.PartitionedNodeByLabelScan ====== .Query @@ -238,9 +249,8 @@ It allows the index to be partitioned into different segments where each segment ---- CYPHER runtime=parallel PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title IS NOT NULL -RETURN r +MATCH (person:Person) +RETURN person ---- .Query Plan @@ -250,40 +260,37 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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: 86 +Total database accesses: 43 ---- ====== -[[query-plan-undirected-relationship-index-scan]] -== Undirected Relationship Index Scan -// UndirectedRelationshipIndexScan - -The `UndirectedRelationshipIndexScan` 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. +[role=label--new-5.5] +[[query-plan-intersection-node-by-labels-scan]] +=== Intersection Node By Labels Scan +The `IntersectionNodeByLabelsScan` operator fetches all nodes that have all of the provided labels from the node label index. -.UndirectedRelationshipIndexScan ====== .Query -[source, cypher] +[source,cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title IS NOT NULL -RETURN r +MATCH (countryAndLocation:Country&Location) +RETURN countryAndLocation ---- .Query Plan @@ -293,43 +300,40 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 30 | 30 | 0 | | | | | -| | +---------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title IS NOT NULL | 30 | 30 | 16 | 120 | 3/1 | 1.266 | Fused in Pipeline 0 | -+----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 16, total allocated memory: 184 ++-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| 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 ---- ====== [role=label--new-5.17] -[[query-plan-partitioned-undirected-relationship-index-scan]] -== Partitioned Undirected Relationship Index Scan -// PartitionedUndirectedRelationshipIndexScan -// New in 5.17 +[[query-plan-partitioned-intersection-node-by-labels-scan]] +=== Partitioned Intersection Node By Labels Scan -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]. +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. -.PartitionedUndirectedRelationshipIndexScan ====== .Query -[source, cypher] +[source,cypher] ---- CYPHER runtime=parallel PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title IS NOT NULL -RETURN r +MATCH (countryAndLocation:Country&Location) +RETURN countryAndLocation ---- .Query Plan @@ -339,42 +343,40 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 +------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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-relationship-index-seek]] -== Directed Relationship Index Seek -// DirectedRelationshipIndexSeek - -The `DirectedRelationshipIndexSeek` 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. +[role=label--new-5.21] +[[query-plan-subtraction-node-by-labels-scan]] +=== Subtraction Node By Labels Scan +The `SubtractionNodeByLabelsScan` operator fetches all nodes that have all of the first set of provided labels and none of the second provided set of labels from the node label index. +In the example below, `SubtractionNodeByLabelsScan` retrieves all nodes that have the `Location` label but do not have the `Country` label. -.DirectedRelationshipIndexSeek ====== .Query -[source, cypher] +[source,cypher] ---- PROFILE -MATCH (candidate)-[r:WORKS_IN]->() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (locationNotCountry:Location&!Country) +RETURN locationNotCountry ---- .Query Plan @@ -384,43 +386,39 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 2 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]->(anon_0) WHERE title = $autostring_0 | 2 | 1 | 2 | 120 | 3/1 | 0.591 | Fused in Pipeline 0 | -+--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | locationNotCountry | 7 | 10 | 0 | 0 | | | | +| | +----+--------------------------------------+----------------+------+---------+----------------+ | | | +| +SubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 248 | 2/0 | 3.081 | Fused in Pipeline 0 | ++------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 2, total allocated memory: 184 +Total database accesses: 13, total allocated memory: 312 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-directed-relationship-index-seek]] -== Partitioned Directed Relationship Index Seek -// PartitionedDirectedRelationshipIndexSeek -// New in 5.17 +[role=label--new-5.21] +[[query-plan-partitioned-subtraction-node-by-labels-scan]] +=== Partitioned Subtraction Node By Labels Scan -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]. +The `PartitionedSubtractionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-subtraction-node-by-labels-scan[`SubtractionNodeByLabelsScan`] 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] +[source,cypher] ---- CYPHER runtime=parallel PROFILE -MATCH (candidate)-[r:WORKS_IN]->() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (locationNotCountry:Location&!Country) +RETURN locationNotCountry ---- .Query Plan @@ -430,42 +428,36 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | locationNotCountry | 7 | 10 | 0 | 136 | 0/0 | 0.614 | In Pipeline 1 | +| | +----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +PartitionedSubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 120 | 2/0 | 5.173 | In Pipeline 0 | ++-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 4 +Total database accesses: 13, total allocated memory: 262144 ---- ====== +[[query-plan-union-node-by-labels-scan]] +=== Union Node By Labels Scan -[[query-plan-undirected-relationship-index-seek]] -== Undirected Relationship Index Seek -// UndirectedRelationshipIndexSeek - -The `UndirectedRelationshipIndexSeek` 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. - +The `UnionNodeByLabelsScan` operator fetches all nodes that have at least one of the provided labels from the node label index. -.UndirectedRelationshipIndexSeek ====== .Query -[source, cypher] +[source,cypher] ---- PROFILE -MATCH (candidate)-[r:WORKS_IN]-() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (countryOrLocation:Country|Location) +RETURN countryOrLocation ---- .Query Plan @@ -475,42 +467,39 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 4 | 2 | 0 | | | | | -| | +----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]-(anon_0) WHERE title = $autostring_0 | 4 | 2 | 2 | 120 | 3/1 | 0.791 | Fused in Pipeline 0 | -+----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +ProduceResults | countryOrLocation | 17 | 11 | 0 | | | | | | +| | +------------------------------------+----------------+------+---------+----------------+ | | | | +| +UnionNodeByLabelsScan | countryOrLocation:Country|Location | 17 | 11 | 13 | 120 | 3/1 | 0.660 | countryOrLocation ASC | Fused in Pipeline 0 | ++------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -Total database accesses: 2, total allocated memory: 184 +Total database accesses: 13, 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. +[role=label--new-5.17] +[[query-plan-partitioned-union-node-by-labels-scan]] +=== Partitioned Union Node By Labels Scan +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. -.PartitionedUndirectedRelationshipIndexSeek ====== .Query -[source, cypher] +[source,cypher] ---- CYPHER runtime=parallel PROFILE -MATCH (candidate)-[r:WORKS_IN]-() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (countryOrLocation:Country|Location) +RETURN countryOrLocation ---- .Query Plan @@ -520,41 +509,46 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 ----- - -====== - ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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 | ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -[[query-plan-directed-relationship-by-element-id-seek]] -== Directed Relationship By Element Id Seek -// DirectedRelationshipByElementIdSeek +Total database accesses: 35 +---- -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. +====== +[[query-plan-node-index-contains-scan]] +=== Node Index Contains Scan -.DirectedRelationshipByElementIdSeek +//// +[source, cypher, role=test-setup] +---- +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`. +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. + +.NodeIndexContainsScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n1)-[r]->() -WHERE elementId(r) = 0 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name CONTAINS 'al' +RETURN l ---- .Query Plan @@ -564,40 +558,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 0 | 2 | 0 | | | | | +| | +---------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexContainsScan | TEXT INDEX l:Location(name) WHERE name CONTAINS $autostring_0 | 0 | 2 | 3 | 120 | 2/0 | 1.305 | Fused in Pipeline 0 | ++------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 312 +Total database accesses: 3, total allocated memory: 184 ---- ====== -[[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 the relationship as well as the source and target node of the relationship. +[[query-plan-node-index-ends-with-scan]] +=== Node Index Ends With Scan + +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. -.DirectedRelationshipByIdSeek +.NodeIndexEndsWithScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n1)-[r]->() -WHERE id(r) = 0 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name ENDS WITH 'al' +RETURN l ---- .Query Plan @@ -607,41 +602,47 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | 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 | -+-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 0 | 0 | 0 | | | | | +| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexEndsWithScan | TEXT INDEX l:Location(name) WHERE name ENDS WITH $autostring_0 | 0 | 0 | 1 | 120 | 0/0 | 4.409 | Fused in Pipeline 0 | ++------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 8, total allocated memory: 312 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[[query-plan-undirected-relationship-by-element-id-seek]] -== Undirected Relationship By Element Id Seek -// UndirectedRelationshipByElementIdSeek -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. +[[query-plan-node-index-scan]] +=== Node Index Scan -.UndirectedRelationshipByElementIdSeek +The `NodeIndexScan` operator examines all values stored in an index, returning all nodes with a particular label and a specified property. + +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_location_name FOR (l:Location) ON (l.name) +---- +//// + +.NodeIndexScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n1)-[r]-() -WHERE elementId(r) = 1 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name IS NOT NULL +RETURN l ---- .Query Plan @@ -651,41 +652,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | | | | | -| | +--------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipByElementIdSeek| (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 | l | 10 | 10 | 0 | | | | | +| | +-----------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | RANGE INDEX l:Location(name) WHERE name IS NOT NULL | 10 | 10 | 11 | 120 | 2/1 | 0.557 | Fused in Pipeline 0 | ++-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 11, 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-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. +[role=label--new-5.17] +[[query-plan-partitioned-node-index-scan]] +=== Partitioned Node Index Scan +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. -.UndirectedRelationshipByIdSeek +.PartitionedNodeIndexScan ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (n1)-[r]-() -WHERE id(r) = 1 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name IS NOT NULL +RETURN l ---- .Query Plan @@ -693,51 +694,42 @@ RETURN r, n1 ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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: 15, total allocated memory: 312 +Total database accesses: 31 ---- ====== -[[query-plan-directed-relationship-index-contains-scan]] -== Directed Relationship Index Contains Scan -// DirectedRelationshipIndexContainsScan - -//// -[source, cypher, role=test-setup] ----- -CREATE TEXT INDEX text_worksin_title FOR ()-[r:WORKS_IN]->() 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`. -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. +[role=label--new-5.3] +[[query-plan-node-by-elementid-seek]] +=== Node By ElementId Seek +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()]. -.DirectedRelationshipIndexContainsScan +.NodeByElementIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title CONTAINS 'senior' -RETURN r +MATCH (n) +WHERE elementId(n) = 0 +RETURN n ---- .Query Plan @@ -747,42 +739,39 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 4 | 0 | | | | | -| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 4 | 5 | 120 | 3/0 | 1.051 | 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: 5, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== +[[query-plan-node-by-id-seek]] +=== Node By Id Seek -[[query-plan-undirected-relationship-index-contains-scan]] -== 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`. -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. +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()]. -.UndirectedRelationshipIndexContainsScan +.NodeByIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title CONTAINS 'senior' -RETURN r +MATCH (n) +WHERE id(n) = 0 +RETURN n ---- .Query Plan @@ -792,42 +781,42 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 8 | 0 | | | | | -| | +-------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 8 | 5 | 120 | 3/0 | 2.684 | Fused in Pipeline 0 | -+------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| 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 -Total database accesses: 5, total allocated memory: 184 ---- ====== -[[query-plan-directed-relationship-index-ends-with-scan]] -== Directed Relationship Index Ends With Scan -// DirectedRelationshipIndexEndsWithScan +[[query-plan-node-index-seek]] +=== Node Index Seek -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. +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 backs up a xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[property uniqueness constraint], the operator is instead called xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-unique-index-seek[NodeUniqueIndexSeek]. -.DirectedRelationshipIndexEndsWithScan +.NodeIndexSeek ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title ENDS WITH 'developer' -RETURN r +MATCH (location:Location {name: 'Malmo'}) +RETURN location ---- .Query Plan @@ -837,42 +826,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 8 | 0 | | | | | -| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 8 | 9 | 120 | 3/0 | 1.887 | Fused in Pipeline 0 | -+----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | location | 1 | 1 | 0 | | | | | +| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.401 | Fused in Pipeline 0 | ++-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 9, total allocated memory: 184 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[role=label--new-5.17] +[[query-plan-partitioned-node-index-seek]] +=== Partitioned Node Index Seek -[[query-plan-undirected-relationship-index-ends-with-scan]] -== Undirected Relationship Index Ends With Scan -// UndirectedRelationshipIndexEndsWithScan +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. -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. - -.UndirectedRelationshipIndexEndsWithScan -====== +.PartitionedNodeIndexSeek +====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title ENDS WITH 'developer' -RETURN r +MATCH (location:Location {name: 'Malmo'}) +RETURN location ---- .Query Plan @@ -880,51 +868,51 @@ RETURN r ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 16 | 0 | | | | | -| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 16 | 9 | 120 | 3/0 | 1.465 | Fused in Pipeline 0 | -+------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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: 9, total allocated memory: 184 +Total database accesses: 4 ---- ====== - -[[query-plan-directed-relationship-index-seek-by-range]] -== Directed Relationship Index Seek By Range -// DirectedRelationshipIndexSeekByRange +[[query-plan-node-unique-index-seek]] +=== Node Unique Index Seek //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX range_worksin_duration FOR ()-[r:WORKS_IN]->() ON (r.duration) +CREATE CONSTRAINT team_name IF NOT EXISTS FOR (t:Team) REQUIRE (t.name) IS UNIQUE ---- //// -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 `+>=+`. +The `NodeUniqueIndexSeek` operator finds nodes using an index seek within an index backing up a xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[property uniqueness constraint]. +The node variable and the index used are shown in the arguments of the operator. +If the index does not back up a property uniqueness constraint, the operator is instead called xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[NodeIndexSeek]. +If the index seek is used to solve a xref::clauses/merge.adoc[MERGE] clause, it will also be marked with `(Locking)`. +This makes it clear that any nodes returned from the index will be locked in order to prevent concurrent conflicting updates. -.DirectedRelationshipIndexSeekByRange +.NodeUniqueIndexSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]->(location) -WHERE r.duration > 100 -RETURN candidate +MATCH (t:Team {name: 'Malmo'}) +RETURN t ---- .Query Plan @@ -934,45 +922,54 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 4 | 15 | 0 | | | | | -| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | candidate:Person | 4 | 15 | 30 | | | | | -| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]->(location) WHERE duration > $autoint_0 | 4 | 15 | 16 | 120 | 4/1 | 0.703 | Fused in Pipeline 0 | -+---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | t | 1 | 0 | 0 | | | | | +| | +------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeUniqueIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 0 | 1 | 120 | 0/1 | 0.280 | Fused in Pipeline 0 | ++----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 46, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-directed-relationship-index-seek-by-range]] -== Partitioned Directed Relationship Index Seek By Range -// PartitionedDirectedRelationshipIndexSeekByRange -// New in 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. +[[query-plan-multi-node-index-seek]] +=== Multi Node Index Seek -.PartitionedDirectedRelationshipIndexSeekByRange +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name) +---- +//// + +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. + +The operator yields a cartesian product of all index seeks. +For example, if the operator does two seeks and the first seek finds the nodes `a1, a2` and the second `b1, b2, b3`, the `MultiNodeIndexSeek` will yield the rows `(a1, b1), (a1, b2), (a1, b3), (a2, b1), (a2, b2), (a2, b3)`. + + +.MultiNodeIndexSeek ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]->(location) -WHERE r.duration > 100 -RETURN candidate +CYPHER runtime=pipelined +MATCH + (location:Location {name: 'Malmo'}), + (person:Person {name: 'Bob'}) +RETURN location, person ---- .Query Plan @@ -980,46 +977,50 @@ RETURN candidate ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | location, person | 1 | 1 | 0 | | | | | +| | +-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +MultiNodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0, | 1 | 0 | 0 | 120 | 2/2 | 1.910 | Fused in Pipeline 0 | +| | RANGE INDEX person:Person(name) WHERE name = $autostring_1 | | | | | | | | ++---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 76 +Total database accesses: 0, total allocated memory: 184 ---- ====== -[[query-plan-undirected-relationship-index-seek-by-range]] -== Undirected Relationship Index Seek By Range -// UndirectedRelationshipIndexSeekByRange +[[query-plan-asserting-multi-node-index-seek]] +=== Asserting Multi Node Index Seek -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 `+>=+`. +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT team_id IF NOT EXISTS FOR (t:Team) REQUIRE (t.id) IS UNIQUE +---- +//// +The `AssertingMultiNodeIndexSeek` operator is used to ensure that no xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[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. -.UndirectedRelationshipIndexSeekByRange + +.AssertingMultiNodeIndexSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]-(location) -WHERE r.duration > 100 -RETURN candidate +MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan @@ -1029,45 +1030,46 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 5 | 15 | 0 | | | | | -| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | candidate:Person | 5 | 15 | 60 | | | | | -| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]-(location) WHERE duration > $autoint_0 | 8 | 30 | 16 | 120 | 4/1 | 1.214 | Fused in Pipeline 0 | -+-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | | 1 | 0 | 0 | | | | | +| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | | 1 | 0 | 0 | | | | | +| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | | | | | +| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +AssertingMultiNodeIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0, UNIQUE t:Team(id) WHERE id = $autoint_1 | 0 | 2 | 4 | 120 | 0/2 | 1.584 | Fused in Pipeline 0 | ++------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 76, total allocated memory: 184 +Total database accesses: 4, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-undirected-relationship-index-seek-by-range]] -== Partitioned Undirected Relationship Index Seek By Range -// PartitionedUndirectedRelationshipIndexSeekByRange -// New in 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. +[[query-plan-node-index-seek-by-range]] +=== Node Index Seek By Range -.PartitionedUndirectedRelationshipIndexSeekByRange +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`. + + +.NodeIndexSeekByRange ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]-(location) -WHERE r.duration > 100 -RETURN candidate +MATCH (l:Location) +WHERE l.name STARTS WITH 'Lon' +RETURN l ---- .Query Plan @@ -1075,42 +1077,44 @@ RETURN candidate ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 2 | 1 | 0 | | | | | +| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeekByRange | RANGE INDEX l:Location(name) WHERE name STARTS WITH $autostring_0 | 2 | 1 | 2 | 120 | 3/0 | 0.825 | Fused in Pipeline 0 | ++-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 106 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[role=label--new-5.17] +[[query-plan-partitioned-node-index-seek-by-range]] +=== Partitioned Node Index Seek By Range -[[query-plan-union-node-by-labels-scan]] -== Union Node By Labels Scan -// UnionNodeByLabelsScan -// New in 5.0 +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. -The `UnionNodeByLabelsScan` operator fetches all nodes that have at least one of the provided labels from the node label index. + +.PartitionedNodeIndexSeekByRange ====== .Query -[source,cypher] +[source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (countryOrLocation:Country|Location) -RETURN countryOrLocation +MATCH (l:Location) +WHERE l.name STARTS WITH 'Lon' +RETURN l ---- .Query Plan @@ -1118,42 +1122,44 @@ RETURN countryOrLocation ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +ProduceResults | countryOrLocation | 17 | 11 | 0 | | | | | | -| | +------------------------------------+----------------+------+---------+----------------+ | | | | -| +UnionNodeByLabelsScan | countryOrLocation:Country|Location | 17 | 11 | 13 | 120 | 3/1 | 0.660 | countryOrLocation ASC | Fused in Pipeline 0 | -+------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ ++----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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: 13, total allocated memory: 184 +Total database accesses: 4 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-union-node-by-labels-scan]] -== Partitioned Union Node By Labels Scan -// PartitionedUnionNodeByLabelsScan -// New in 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-plan-node-unique-index-seek-by-range]] +=== Node Unique Index Seek By Range + +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`. + + +.NodeUniqueIndexSeekByRange ====== .Query -[source,cypher] +[source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (countryOrLocation:Country|Location) -RETURN countryOrLocation +MATCH (t:Team) +WHERE t.name STARTS WITH 'Ma' +RETURN t ---- .Query Plan @@ -1161,199 +1167,27 @@ RETURN countryOrLocation ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 ----- - -====== - - -[role=label--new-5.5] -[[query-plan-intersection-node-by-labels-scan]] -== Intersection Node By Labels Scan -// IntersectionNodeByLabelsScan - -The `IntersectionNodeByLabelsScan` operator fetches all nodes that have all of the provided labels from the node label index. -====== - -.Query -[source,cypher] ----- -PROFILE -MATCH (countryAndLocation:Country&Location) -RETURN countryAndLocation ----- - -.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 | 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 ----- - -====== - -[role=label--new-5.17] -[[query-plan-partitioned-intersection-node-by-labels-scan]] -== Partitioned Intersection Node By Labels Scan -// PartitionedIntersectionNodeByLabelsScan -// New in 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 ----- - -====== - -[role=label--new-5.21] -[[query-plan-subtraction-node-by-labels-scan]] -== Subtraction Node By Labels Scan -// SubtractionNodeByLabelsScan -// New in 5.21 - -The `SubtractionNodeByLabelsScan` operator fetches all nodes that have all of the first set of provided labels and none of the second provided set of labels from the node label index. -In the example below, `SubtractionNodeByLabelsScan` retrieves all nodes that have the `Location` label but do not have the `Country` label. -====== - -.Query -[source,cypher] ----- -PROFILE -MATCH (locationNotCountry:Location&!Country) -RETURN locationNotCountry ----- - -.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 | locationNotCountry | 7 | 10 | 0 | 0 | | | | -| | +----+--------------------------------------+----------------+------+---------+----------------+ | | | -| +SubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 248 | 2/0 | 3.081 | Fused in Pipeline 0 | -+------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 13, total allocated memory: 312 ----- - -====== - -[role=label--new-5.21] -[[query-plan-partitioned-subtraction-node-by-labels-scan]] -== Partitioned Subtraction Node By Labels Scan -// PartitionedSubtractionNodeByLabelsScan -// New in 5.21 - -The `PartitionedSubtractionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-subtraction-node-by-labels-scan[`SubtractionNodeByLabelsScan`] 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 (locationNotCountry:Location&!Country) -RETURN locationNotCountry ----- - -.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 | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | locationNotCountry | 7 | 10 | 0 | 136 | 0/0 | 0.614 | In Pipeline 1 | -| | +----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +PartitionedSubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 120 | 2/0 | 5.173 | In Pipeline 0 | -+-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | t | 2 | 0 | 0 | | | | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeUniqueIndexSeekByRange | UNIQUE t:Team(name) WHERE name STARTS WITH $autostring_0 | 2 | 0 | 1 | 120 | 1/0 | 0.623 | Fused in Pipeline 0 | ++-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 13, total allocated memory: 262144 +Total database accesses: 1, total allocated memory: 184 ---- ====== [[query-plan-directed-all-relationships-scan]] -== Directed All Relationships Scan -//DirectedAllRelationshipsScan +=== Directed All Relationships Scan The `DirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. @@ -1373,7 +1207,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1392,8 +1226,7 @@ Total database accesses: 28, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-directed-all-relationships-scan]] -== Partitioned Directed All Relationships Scan -// New in 5.17 +=== Partitioned Directed All Relationships Scan 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. @@ -1415,7 +1248,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1432,8 +1265,8 @@ Total database accesses: 111 ====== [[query-plan-undirected-all-relationships-scan]] -== Undirected All Relationships Scan -//UndirectedAllRelationshipsScan +=== Undirected All Relationships Scan + The `UndirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. ====== @@ -1451,7 +1284,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1470,7 +1303,7 @@ Total database accesses: 28, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-undirected-all-relationships-scan]] -== Partitioned Undirected All Relationships Scan +=== Partitioned Undirected All Relationships Scan // New in 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]. @@ -1492,7 +1325,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1510,7 +1343,7 @@ Total database accesses: 194 ====== [[query-plan-directed-relationship-type-scan]] -== 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. @@ -1534,7 +1367,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1553,7 +1386,7 @@ Total database accesses: 13, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-directed-relationship-types-scan]] -== Partitioned Directed Relationship Type Scan +=== Partitioned Directed Relationship Type Scan // PartitionedDirectedRelationshipTypeScan // New in 5.17 @@ -1579,7 +1412,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1598,7 +1431,7 @@ Batch size 128 [[query-plan-undirected-relationship-type-scan]] -== 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. @@ -1620,7 +1453,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1639,7 +1472,7 @@ Total database accesses: 13, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-undirected-relationship-type-scan]] -== Partitioned Undirected Relationship Type Scan +=== Partitioned Undirected Relationship Type Scan // PartitionedUndirectedRelationshipTypeScan // New in 5.17 @@ -1664,7 +1497,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1683,13 +1516,13 @@ Total database accesses: 37 [[query-plan-directed-union-relationship-types-scan]] -== Directed Union Relationship Types Scan +=== Directed Union Relationship Types Scan // DirectedUnionRelationshipTypesScan 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. [NOTE] -As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. +As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. .DirectedUnionRelationshipTypesScan ====== @@ -1709,7 +1542,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1728,7 +1561,7 @@ Total database accesses: 14, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-directed-union-relationship-types-scan]] -== Partitioned Directed Union Relationship Types Scan +=== Partitioned Directed Union Relationship Types Scan // PartitionedDirectedUnionRelationshipTypesScan // New in 5.17 @@ -1736,7 +1569,7 @@ The `PartitionedDirectedUnionRelationshipTypeScan` is a variant of the xref:plan It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. [NOTE] -As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. +As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. .PartitionedDirectedUnionRelationshipTypesScan ====== @@ -1757,7 +1590,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1776,13 +1609,13 @@ Total database accesses: 25 [[query-plan-undirected-union-relationship-types-scan]] -== Undirected Union Relationship Types Scan +=== Undirected Union Relationship Types Scan // UndirectedUnionRelationshipTypesScan 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. [NOTE] -As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. +As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. .UndirectedUnionRelationshipTypeScan ====== @@ -1802,7 +1635,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1821,15 +1654,13 @@ Total database accesses: 14, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-undirected-union-relationship-types-scan]] -== Partitioned Undirected Union Relationship Types Scan -// PartitionedUndirectedUnionRelationshipTypesScan -// New in 5.17 +=== Partitioned Undirected Union Relationship Types Scan 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. [NOTE] -As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. +As the block storage format becomes the default, this operator will cease to be used in generating plans. Please refer to link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/store-formats[Operations Manual -> Store formats] for futher details on the various store formats available. .PartitionedUndirectedUnionRelationshipTypesScan ====== @@ -1850,7 +1681,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -1866,23 +1697,31 @@ Total database accesses: 37 ---- ====== -[role=label--new-5.3] -[[query-plan-node-by-elementid-seek]] -== Node By ElementId Seek -// NodeByElementIdSeek -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()]. +[[query-plan-directed-relationship-index-scan]] +=== Directed Relationship Index Scan +// DirectedRelationshipIndexScan -.NodeByElementIdSeek +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) +---- +//// + +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. + + +.DirectedRelationshipIndexScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n) -WHERE elementId(n) = 0 -RETURN n +MATCH ()-[r: WORKS_IN]->() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -1892,40 +1731,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| 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 | -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 15 | 15 | 0 | | | | | +| | +----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title IS NOT NULL | 15 | 15 | 16 | 120 | 3/1 | 2.464 | Fused in Pipeline 0 | ++--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 16, total allocated memory: 184 ---- ====== -[[query-plan-node-by-id-seek]] -== 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-id[id()]. +[role=label--new-5.17] +[[query-plan-partitioned-directed-relationship-index-scan]] +=== Partitioned Directed Relationship Index Scan +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. -.NodeByIdSeek +.PartitionedDirectedRelationshipIndexScan ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (n) -WHERE id(n) = 0 -RETURN n +MATCH ()-[r: WORKS_IN]->() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -1933,44 +1773,41 @@ RETURN n ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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 | ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -1 row +Total database accesses: 86 ---- ====== +[[query-plan-undirected-relationship-index-scan]] +=== Undirected Relationship Index Scan -[[query-plan-node-by-label-scan]] -== Node By Label Scan -// NodeByLabelScan - -The `NodeByLabelScan` operator fetches all nodes with a specific label from the node label index. +The `UndirectedRelationshipIndexScan` 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. -.NodeByLabelScan +.UndirectedRelationshipIndexScan ====== .Query [source, cypher] ---- PROFILE -MATCH (person:Person) -RETURN person +MATCH ()-[r: WORKS_IN]-() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -1980,34 +1817,31 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 14 | 14 | 0 | | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | person:Person | 14 | 14 | 15 | 120 | 2/1 | 0.522 | Fused in Pipeline 0 | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 30 | 30 | 0 | | | | | +| | +---------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title IS NOT NULL | 30 | 30 | 16 | 120 | 3/1 | 1.266 | Fused in Pipeline 0 | ++----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 15, total allocated memory: 184 +Total database accesses: 16, total allocated memory: 184 ---- ====== [role=label--new-5.17] -[[query-plan-partitioned-node-by-label-scan]] -== Partitioned Node By Label Scan -// PartitionedNodeByLabelScan -// New in 5.17 +[[query-plan-partitioned-undirected-relationship-index-scan]] +=== Partitioned Undirected Relationship Index Scan -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]. +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. - -.PartitionedNodeByLabelScan +.PartitionedUndirectedRelationshipIndexScan ====== .Query @@ -2015,8 +1849,9 @@ It allows the index to be partitioned into different segments where each segment ---- CYPHER runtime=parallel PROFILE -MATCH (person:Person) -RETURN person +MATCH ()-[r: WORKS_IN]-() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -2026,49 +1861,48 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ ++---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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: 43 +Total database accesses: 156 ---- ====== -[[query-plan-node-index-seek]] -== Node Index Seek -// NodeIndexSeek +[[query-plan-directed-relationship-index-contains-scan]] +=== Directed Relationship Index Contains Scan //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX range_location_name FOR (l:Location) ON (l.name) +CREATE TEXT INDEX text_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) ---- //// -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::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-unique-index-seek[NodeUniqueIndexSeek]. +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. -.NodeIndexSeek +.DirectedRelationshipIndexContainsScan ====== .Query [source, cypher] ---- PROFILE -MATCH (location:Location {name: 'Malmo'}) -RETURN location +MATCH ()-[r: WORKS_IN]->() +WHERE r.title CONTAINS 'senior' +RETURN r ---- .Query Plan @@ -2078,43 +1912,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | location | 1 | 1 | 0 | | | | | -| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.401 | Fused in Pipeline 0 | -+-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 4 | 0 | | | | | +| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 4 | 5 | 120 | 3/0 | 1.051 | Fused in Pipeline 0 | ++----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 2, total allocated memory: 184 +Total database accesses: 5, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-node-index-seek]] -== Partitioned Node Index Seek -// PartitionedNodeIndexSeek -// New in 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. +[[query-plan-undirected-relationship-index-contains-scan]] +=== Undirected Relationship Index Contains Scan + +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. -.PartitionedNodeIndexSeek +.UndirectedRelationshipIndexContainsScan ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (location:Location {name: 'Malmo'}) -RETURN location +MATCH ()-[r: WORKS_IN]-() +WHERE r.title CONTAINS 'senior' +RETURN r ---- .Query Plan @@ -2122,53 +1954,43 @@ RETURN location ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 8 | 0 | | | | | +| | +-------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 8 | 5 | 120 | 3/0 | 2.684 | Fused in Pipeline 0 | ++------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 4 +Total database accesses: 5, total allocated memory: 184 ---- ====== -[[query-plan-node-unique-index-seek]] -== Node Unique Index Seek -// NodeUniqueIndexSeek - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT team_name IF NOT EXISTS FOR (t:Team) REQUIRE (t.name) IS UNIQUE ----- -//// +[[query-plan-directed-relationship-index-ends-with-scan]] +=== Directed Relationship Index Ends With Scan -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::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[NodeIndexSeek]. -If the index seek is used to solve a xref::clauses/merge.adoc[MERGE] clause, it will also be marked with `(Locking)`. -This makes it clear that any nodes returned from the index will be locked in order to prevent concurrent conflicting updates. +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. -.NodeUniqueIndexSeek +.DirectedRelationshipIndexEndsWithScan ====== .Query [source, cypher] ---- PROFILE -MATCH (t:Team {name: 'Malmo'}) -RETURN t +MATCH ()-[r: WORKS_IN]->() +WHERE r.title ENDS WITH 'developer' +RETURN r ---- .Query Plan @@ -2178,55 +2000,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | t | 1 | 0 | 0 | | | | | -| | +------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeUniqueIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 0 | 1 | 120 | 0/1 | 0.280 | Fused in Pipeline 0 | -+----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 8 | 0 | | | | | +| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 8 | 9 | 120 | 3/0 | 1.887 | Fused in Pipeline 0 | ++----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 9, total allocated memory: 184 ---- ====== -[[query-plan-multi-node-index-seek]] -== Multi Node Index Seek -// MultiNodeIndexSeek - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name) ----- -//// - -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. +[[query-plan-undirected-relationship-index-ends-with-scan]] +=== Undirected Relationship Index Ends With Scan -The operator yields a cartesian product of all index seeks. -For example, if the operator does two seeks and the first seek finds the nodes `a1, a2` and the second `b1, b2, b3`, the `MultiNodeIndexSeek` will yield the rows `(a1, b1), (a1, b2), (a1, b3), (a2, b1), (a2, b2), (a2, b3)`. +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. -.MultiNodeIndexSeek +.UndirectedRelationshipIndexEndsWithScan ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=pipelined -MATCH - (location:Location {name: 'Malmo'}), - (person:Person {name: 'Bob'}) -RETURN location, person +MATCH ()-[r: WORKS_IN]-() +WHERE r.title ENDS WITH 'developer' +RETURN r ---- .Query Plan @@ -2236,49 +2044,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | location, person | 1 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +MultiNodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0, | 1 | 0 | 0 | 120 | 2/2 | 1.910 | Fused in Pipeline 0 | -| | RANGE INDEX person:Person(name) WHERE name = $autostring_1 | | | | | | | | -+---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 16 | 0 | | | | | +| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 16 | 9 | 120 | 3/0 | 1.465 | Fused in Pipeline 0 | ++------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 184 +Total database accesses: 9, total allocated memory: 184 ---- ====== -[[query-plan-asserting-multi-node-index-seek]] -== Asserting Multi Node Index Seek -// AssertingMultiNodeIndexSeek - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT team_id IF NOT EXISTS FOR (t:Team) REQUIRE (t.id) IS UNIQUE ----- -//// +[[query-plan-directed-relationship-index-seek]] +=== Directed Relationship Index Seek -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. +The `DirectedRelationshipIndexSeek` 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. -.AssertingMultiNodeIndexSeek +.DirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- PROFILE -MERGE (t:Team {name: 'Engineering', id: 42}) +MATCH (candidate)-[r:WORKS_IN]->() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2288,48 +2088,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | | 1 | 0 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | | 1 | 0 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +AssertingMultiNodeIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0, UNIQUE t:Team(id) WHERE id = $autoint_1 | 0 | 2 | 4 | 120 | 0/2 | 1.584 | Fused in Pipeline 0 | -+------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 2 | 1 | 0 | | | | | +| | +-----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]->(anon_0) WHERE title = $autostring_0 | 2 | 1 | 2 | 120 | 3/1 | 0.591 | Fused in Pipeline 0 | ++--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 4, total allocated memory: 184 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[role=label--new-5.17] +[[query-plan-partitioned-directed-relationship-index-seek]] +=== Partitioned Directed Relationship Index Seek -[[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`. - +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. -.NodeIndexSeekByRange +.PartitionedDirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (l:Location) -WHERE l.name STARTS WITH 'Lon' -RETURN l +MATCH (candidate)-[r:WORKS_IN]->() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2337,46 +2130,43 @@ RETURN l ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 2 | 1 | 0 | | | | | -| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeekByRange | RANGE INDEX l:Location(name) WHERE name STARTS WITH $autostring_0 | 2 | 1 | 2 | 120 | 3/0 | 0.825 | Fused in Pipeline 0 | -+-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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: 2, total allocated memory: 184 +Total database accesses: 4 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-node-index-seek-by-range]] -== Partitioned Node Index Seek By Range -// PartitionedNodeIndexSeekByRange -// New in 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. +[[query-plan-undirected-relationship-index-seek]] +=== Undirected Relationship Index Seek +The `UndirectedRelationshipIndexSeek` 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. -.PartitionedNodeIndexSeekByRange + +.UndirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (l:Location) -WHERE l.name STARTS WITH 'Lon' -RETURN l +MATCH (candidate)-[r:WORKS_IN]-() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2384,45 +2174,43 @@ RETURN l ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 4 | 2 | 0 | | | | | +| | +----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]-(anon_0) WHERE title = $autostring_0 | 4 | 2 | 2 | 120 | 3/1 | 0.791 | Fused in Pipeline 0 | ++----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 4 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[[query-plan-partitioned-undirected-relationship-index-seek]] +=== Partitioned Undirected Relationship Index Seek -[[query-plan-node-unique-index-seek-by-range]] -== 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`. -`NodeUniqueIndexSeekByRange` is used by `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+`, and `+>=+`. -If the index is not unique, the operator is instead called `NodeIndexSeekByRange`. +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. -.NodeUniqueIndexSeekByRange +.PartitionedUndirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (t:Team) -WHERE t.name STARTS WITH 'Ma' -RETURN t +MATCH (candidate)-[r:WORKS_IN]-() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2430,51 +2218,42 @@ RETURN t ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | t | 2 | 0 | 0 | | | | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeUniqueIndexSeekByRange | UNIQUE t:Team(name) WHERE name STARTS WITH $autostring_0 | 2 | 0 | 1 | 120 | 1/0 | 0.623 | Fused in Pipeline 0 | -+-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| 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: 1, total allocated memory: 184 +Total database accesses: 6 ---- ====== -[[query-plan-node-index-contains-scan]] -== Node Index Contains Scan == -// NodeIndexContainsScan - -//// -[source, cypher, role=test-setup] ----- -CREATE TEXT INDEX text_location_name FOR (l:Location) ON (l.name) ----- -//// +[[query-plan-directed-relationship-by-element-id-seek]] +=== Directed Relationship By Element Id Seek -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. +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. -.NodeIndexContainsScan +.DirectedRelationshipByElementIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (l:Location) -WHERE l.name CONTAINS 'al' -RETURN l +MATCH (n1)-[r]->() +WHERE elementId(r) = 0 +RETURN r, n1 ---- .Query Plan @@ -2484,42 +2263,39 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 0 | 2 | 0 | | | | | -| | +---------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexContainsScan | TEXT INDEX l:Location(name) WHERE name CONTAINS $autostring_0 | 0 | 2 | 3 | 120 | 2/0 | 1.305 | 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 | 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: 3, total allocated memory: 184 +Total database accesses: 0, total allocated memory: 312 ---- ====== +[[query-plan-directed-relationship-by-id-seek]] +=== Directed Relationship By Id Seek -[[query-plan-node-index-ends-with-scan]] -== 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`. -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. +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. -.NodeIndexEndsWithScan +.DirectedRelationshipByIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (l:Location) -WHERE l.name ENDS WITH 'al' -RETURN l +MATCH (n1)-[r]->() +WHERE id(r) = 0 +RETURN r, n1 ---- .Query Plan @@ -2529,41 +2305,40 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 0 | 0 | 0 | | | | | -| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexEndsWithScan | TEXT INDEX l:Location(name) WHERE name ENDS WITH $autostring_0 | 0 | 0 | 1 | 120 | 0/0 | 4.409 | 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 -[[query-plan-node-index-scan]] -== Node Index Scan -// NodeIndexScan - -The `NodeIndexScan` operator examines all values stored in an index, returning all nodes with a particular label and a specified property. +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. -.NodeIndexScan +.UndirectedRelationshipByElementIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (l:Location) -WHERE l.name IS NOT NULL -RETURN l +MATCH (n1)-[r]-() +WHERE elementId(r) = 1 +RETURN r, n1 ---- .Query Plan @@ -2573,44 +2348,40 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 10 | 10 | 0 | | | | | -| | +-----------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexScan | RANGE INDEX l:Location(name) WHERE name IS NOT NULL | 10 | 10 | 11 | 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, 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: 11, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-node-index-scan]] -== Partitioned Node Index Scan -// PartitionedNodeIndexScan -// New in 5.17 +[[query-plan-undirected-relationship-by-id-seek]] +=== Undirected Relationship By Id Seek -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. +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. -.PartitionedNodeIndexScan +.UndirectedRelationshipByIdSeek ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (l:Location) -WHERE l.name IS NOT NULL -RETURN l +MATCH (n1)-[r]-() +WHERE id(r) = 1 +RETURN r, n1 ---- .Query Plan @@ -2618,47 +2389,50 @@ RETURN l ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| 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: 31 +Total database accesses: 15, total allocated memory: 312 ---- ====== -// --- apply operators --- +[[query-plan-directed-relationship-index-seek-by-range]] +=== Directed Relationship Index Seek By Range -[[query-plan-apply]] -== Apply -// Apply +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_worksin_duration FOR ()-[r:WORKS_IN]->() ON (r.duration) +---- +//// -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. +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 `+>=+`. -.Apply +.DirectedRelationshipIndexSeekByRange ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person {name: 'me'}) -MATCH (q:Person {name: p.secondName}) -RETURN p, q +MATCH (candidate: Person)-[r:WORKS_IN]->(location) +WHERE r.duration > 100 +RETURN candidate ---- .Query Plan @@ -2668,31 +2442,221 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 1 | 0 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | | 1 | 0 | 0 | | | | | -| |\ +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = p.secondName | 1 | 0 | 0 | 2152 | 0/0 | 0.219 | Fused in Pipeline 1 | -| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.236 | In Pipeline 0 | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 2216 ----- - ++---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 4 | 15 | 0 | | | | | +| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | candidate:Person | 4 | 15 | 30 | | | | | +| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]->(location) WHERE duration > $autoint_0 | 4 | 15 | 16 | 120 | 4/1 | 0.703 | Fused in Pipeline 0 | ++---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 46, total allocated memory: 184 +---- + +====== + +[role=label--new-5.17] +[[query-plan-partitioned-directed-relationship-index-seek-by-range]] +=== Partitioned Directed Relationship Index Seek By Range + +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} + +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 + +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 `+>=+`. + + +.UndirectedRelationshipIndexSeekByRange +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (candidate: Person)-[r:WORKS_IN]-(location) +WHERE r.duration > 100 +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version} + +Batch size 128 + ++-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 5 | 15 | 0 | | | | | +| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | candidate:Person | 5 | 15 | 60 | | | | | +| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]-(location) WHERE duration > $autoint_0 | 8 | 30 | 16 | 120 | 4/1 | 1.214 | Fused in Pipeline 0 | ++-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 76, total allocated memory: 184 +---- + +====== + +[role=label--new-5.17] +[[query-plan-partitioned-undirected-relationship-index-seek-by-range]] +=== Partitioned Undirected Relationship Index Seek By Range + +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} + +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 +---- + +====== + + +[[nested-loops-join-operators]] +== Nested loops and join operators + +Nested loop operators process data by iterating over the right-hand side (RHS) for each row from the left-hand side (LHS). +Each row from the LHS triggers the execution of the RHS, effectively creating a loop over the RHS for each LHS element. + +[[query-plan-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. +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. + + +.Apply +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (p:Person {name: 'me'}) +MATCH (q:Person {name: p.secondName}) +RETURN p, q +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version} + +Batch size 128 + ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, q | 1 | 0 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | | 1 | 0 | 0 | | | | | +| |\ +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = p.secondName | 1 | 0 | 0 | 2152 | 0/0 | 0.219 | Fused in Pipeline 1 | +| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.236 | In Pipeline 0 | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 2, total allocated memory: 2216 +---- + ====== [[query-plan-semi-apply]] -== Semi Apply -// SemiApply +=== Semi Apply 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. @@ -2719,7 +2683,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-----------------+-------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -2746,7 +2710,7 @@ Total database accesses: 142, total allocated memory: 64 [[query-plan-anti-semi-apply]] -== Anti Semi Apply +=== 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. @@ -2775,7 +2739,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | @@ -2802,29 +2766,28 @@ Total database accesses: 166, total allocated memory: 976 ====== -[[query-plan-transaction-apply]] -== TransactionApply -// TransactionApply -`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 -====== +[[query-plan-let-semi-apply]] +=== Let Semi Apply -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. +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. + + +.LetSemiApply +====== .Query [source, cypher] ---- PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -CALL (line) { - CREATE (a: Artist {name: line[0]}) - RETURN a -} IN TRANSACTIONS OF 100 ROWS -RETURN a; +CYPHER runtime=slotted +MATCH (other:Person) +WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) +RETURN other.name ---- .Query Plan @@ -2832,154 +2795,35 @@ RETURN a; ---- Planner COST -Runtime PIPELINED +Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} -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 -// Anti - -The `Anti` operator tests for the absence of a pattern. -If there are incoming rows, the `Anti` operator will yield no rows. -If there are no incoming rows, the `Anti` operator will yield a single row. - - -.Anti -====== - -.Query -[source, cypher] ----- -PROFILE -CYPHER runtime=pipelined -MATCH - (me:Person {name: 'me'}), - (other:Person) -WHERE NOT (me)-[:FRIENDS_WITH]->(other) -RETURN other.name ----- - -.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 | `other.name` | 4 | 12 | 0 | | 0/0 | 0.068 | | -| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | other.name AS `other.name` | 4 | 12 | 24 | | 2/0 | 0.111 | In Pipeline 4 | -| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Apply | | 4 | 12 | 0 | | 0/0 | | | -| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Anti | | 4 | 12 | 0 | 1256 | 0/0 | 0.084 | In Pipeline 4 | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Limit | 1 | 11 | 2 | 0 | 752 | | | | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(Into) | (me)-[anon_2:FRIENDS_WITH]->(other) | 1 | 2 | 81 | 2632 | | | | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | me, other | 14 | 14 | 0 | 3192 | 1/0 | 0.904 | Fused in Pipeline 3 | -| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +CartesianProduct | | 14 | 14 | 0 | 3672 | | 1.466 | In Pipeline 2 | -| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +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 | -+-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 178, total allocated memory: 6744 ----- - -====== - - -[[query-plan-let-semi-apply]] -== 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. -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. - - -.LetSemiApply -====== - -.Query -[source, cypher] ----- -PROFILE -CYPHER runtime=slotted -MATCH (other:Person) -WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) -RETURN other.name ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime SLOTTED - -Runtime version {neo4j-version-minor} - -+--------------------+-----------------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+--------------------+-----------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | `other.name` | 13 | 14 | 0 | 0/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +Projection | other.name AS `other.name` | 13 | 14 | 14 | 1/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +SelectOrSemiApply | anon_9 | 14 | 14 | 0 | 0/0 | -| |\ +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Filter | anon_7:Location | 14 | 0 | 4 | 0/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Expand(All) | (other)-[anon_6:WORKS_IN]->(anon_7) | 14 | 4 | 15 | 8/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Argument | other | 14 | 4 | 0 | 0/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +LetSemiApply | | 14 | 14 | 0 | 0/0 | -| |\ +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Filter | anon_5:Person | 12 | 0 | 10 | 0/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Expand(All) | (other)-[anon_4:FRIENDS_WITH]->(anon_5) | 12 | 10 | 51 | 28/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Argument | other | 14 | 14 | 0 | 0/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +NodeByLabelScan | other:Person | 14 | 14 | 35 | 1/0 | -+--------------------+-----------------------------------------+----------------+------+---------+------------------------+ ++--------------------+-----------------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++--------------------+-----------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | `other.name` | 13 | 14 | 0 | 0/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +Projection | other.name AS `other.name` | 13 | 14 | 14 | 1/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +SelectOrSemiApply | anon_9 | 14 | 14 | 0 | 0/0 | +| |\ +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Filter | anon_7:Location | 14 | 0 | 4 | 0/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Expand(All) | (other)-[anon_6:WORKS_IN]->(anon_7) | 14 | 4 | 15 | 8/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Argument | other | 14 | 4 | 0 | 0/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +LetSemiApply | | 14 | 14 | 0 | 0/0 | +| |\ +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Filter | anon_5:Person | 12 | 0 | 10 | 0/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Expand(All) | (other)-[anon_4:FRIENDS_WITH]->(anon_5) | 12 | 10 | 51 | 28/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Argument | other | 14 | 14 | 0 | 0/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +NodeByLabelScan | other:Person | 14 | 14 | 35 | 1/0 | ++--------------------+-----------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 165, total allocated memory: 64 ---- @@ -2988,8 +2832,7 @@ Total database accesses: 165, total allocated memory: 64 [[query-plan-let-anti-semi-apply]] -== Let Anti Semi Apply -// LetAntiSemiApply +=== Let Anti Semi Apply 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. @@ -3017,7 +2860,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ @@ -3051,8 +2894,7 @@ Total database accesses: 142, total allocated memory: 64 [[query-plan-select-or-semi-apply]] -== Select Or Semi Apply -// SelectOrSemiApply +=== Select Or Semi Apply 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. @@ -3079,7 +2921,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -3110,8 +2952,7 @@ Total database accesses: 148, total allocated memory: 2952 [[query-plan-select-or-anti-semi-apply]] -== Select Or Anti Semi Apply -// SelectOrAntiSemiApply +=== Select Or Anti Semi Apply 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. @@ -3137,7 +2978,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -3170,8 +3011,7 @@ Total database accesses: 136, total allocated memory: 4208 [[query-plan-let-select-or-semi-apply]] -== Let Select Or Semi Apply -// LetSelectOrSemiApply +=== Let Select Or Semi Apply 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. @@ -3197,7 +3037,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-----------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -3232,8 +3072,7 @@ Total database accesses: 179, total allocated memory: 64 [[query-plan-let-select-or-anti-semi-apply]] -== Let Select Or Anti Semi Apply -// LetSelectOrAntiSemiApply +=== Let Select Or Anti Semi Apply 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. @@ -3259,7 +3098,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +---------------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -3292,29 +3131,81 @@ Total database accesses: 208, total allocated memory: 64 ====== +[[query-plan-roll-up-apply]] +=== Roll Up Apply -[[query-plan-merge]] -== Merge -// ConditionalApply -- changed in 4.3 to Merge. -// AntiConditionalApply -- removed in 4.3 (by Merge). -// Merge +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. -The `Merge` operator will either read or create nodes and/or relationships. -If matches are found it will execute the provided `ON MATCH` operations foreach incoming row. -If no matches are found instead nodes and relationships are created and all `ON CREATE` operations are run. +.RollUpApply +====== + +.Query +[source, cypher] +---- +PROFILE +CYPHER runtime=slotted +MATCH (p:Person) +RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST +Runtime SLOTTED -.Merge +Runtime version {neo4j-version} + ++-----------------+-----------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++-----------------+-----------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | `p.name`, cities | 14 | 14 | 0 | 0/0 | +| | +-----------------------------------+----------------+------+---------+------------------------+ +| +Projection | p.name AS `p.name` | 14 | 14 | 14 | 0/0 | +| | +-----------------------------------+----------------+------+---------+------------------------+ +| +RollUpApply | cities, anon_0 | 14 | 14 | 0 | 0/0 | +| |\ +-----------------------------------+----------------+------+---------+------------------------+ +| | +Projection | location.name AS anon_0 | 15 | 15 | 15 | 1/0 | +| | | +-----------------------------------+----------------+------+---------+------------------------+ +| | +Expand(All) | (p)-[anon_2:WORKS_IN]->(location) | 15 | 15 | 53 | 28/0 | +| | | +-----------------------------------+----------------+------+---------+------------------------+ +| | +Argument | p | 14 | 14 | 0 | 0/0 | +| | +-----------------------------------+----------------+------+---------+------------------------+ +| +NodeByLabelScan| p:Person | 14 | 14 | 35 | 1/0 | ++-----------------+-----------------------------------+----------------+------+---------+------------------------+ + +Total database accesses: 153, total allocated memory: 64 +---- + +====== + + + +[[query-plan-transaction-apply]] +=== TransactionApply + +`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 ====== +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + .Query [source, cypher] ---- PROFILE -MERGE (p:Person {name: 'Andy'}) -ON MATCH SET p.existed = true -ON CREATE SET p.existed = false +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL (line) { + CREATE (a: Artist {name: line[0]}) + RETURN a +} IN TRANSACTIONS OF 100 ROWS +RETURN a; ---- .Query Plan @@ -3324,37 +3215,37 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | | 1 | 0 | 0 | | | | | -| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | | 1 | 0 | 0 | | | | | -| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Merge | CREATE (p:Person {name: $autostring_0}), ON MATCH SET p.existed = true, | 1 | 1 | 2 | | | | | -| | | ON CREATE SET p.existed = false | | | | | | | | -| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.749 | Fused in Pipeline 0 | -+-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| 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: 4, total allocated memory: 184 +Total database accesses: 24, total allocated memory: 5472 ---- ====== -[[query-plan-locking-merge]] -== Locking Merge -// LockingMerge +[[query-plan-argument]] +=== Argument -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. +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. -.LockingMerge +.Argument ====== .Query @@ -3372,7 +3263,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -3399,76 +3290,29 @@ Total database accesses: 15, total allocated memory: 2232 ====== +[[query-plan-argument-tracker]] +=== Argument Tracker -[[query-plan-roll-up-apply]] -== Roll Up Apply -// 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. - +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. -.RollUpApply +.ArgumentTracker ====== +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -MATCH (p:Person) -RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime SLOTTED - -Runtime version {neo4j-version-minor} - -+-----------------+-----------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+-----------------+-----------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | `p.name`, cities | 14 | 14 | 0 | 0/0 | -| | +-----------------------------------+----------------+------+---------+------------------------+ -| +Projection | p.name AS `p.name` | 14 | 14 | 14 | 0/0 | -| | +-----------------------------------+----------------+------+---------+------------------------+ -| +RollUpApply | cities, anon_0 | 14 | 14 | 0 | 0/0 | -| |\ +-----------------------------------+----------------+------+---------+------------------------+ -| | +Projection | location.name AS anon_0 | 15 | 15 | 15 | 1/0 | -| | | +-----------------------------------+----------------+------+---------+------------------------+ -| | +Expand(All) | (p)-[anon_2:WORKS_IN]->(location) | 15 | 15 | 53 | 28/0 | -| | | +-----------------------------------+----------------+------+---------+------------------------+ -| | +Argument | p | 14 | 14 | 0 | 0/0 | -| | +-----------------------------------+----------------+------+---------+------------------------+ -| +NodeByLabelScan| p:Person | 14 | 14 | 35 | 1/0 | -+-----------------+-----------------------------------+----------------+------+---------+------------------------+ - -Total database accesses: 153, total allocated memory: 64 ----- - -====== - - -[[query-plan-argument]] -== 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. - - -.Argument -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (s:Person {name: 'me'}) -MERGE (s)-[:FRIENDS_WITH]->(s) +MATCH (s:Person {name: 'me'}) +CALL (s) { + SET s.seen = coalesce(s.seen + 1,1) + RETURN s.seen AS result +} +RETURN result; ---- .Query Plan @@ -3478,67 +3322,7 @@ 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 | | 1 | 0 | 0 | | | | | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | 1 | | 1 | 0 | 0 | | | | | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 2 | | 1 | 1 | 0 | | | | | -| |\ +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +LockingMerge | 3 | CREATE (s)-[anon_0:FRIENDS_WITH]->(s), LOCK(s) | 1 | 1 | 1 | | | | | -| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(Into) | 4 | (s)-[anon_0:FRIENDS_WITH]->(s) | 0 | 0 | 10 | 904 | | | | -| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 5 | s | 1 | 3 | 0 | 2280 | 2/0 | 0.460 | Fused in Pipeline 1 | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeIndexSeek | 6 | RANGE INDEX s:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 1/0 | 0.211 | In Pipeline 0 | -+-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -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 -====== - -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. - -.Query -[source, cypher] ----- -PROFILE -MATCH (s:Person {name: 'me'}) -CALL (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} +Runtime version {neo4j-version} +--------------------+----+---------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -3566,71 +3350,24 @@ Total database accesses: 6, total allocated memory: 4136 ====== -// --- expand operators --- - -[[query-plan-expand-all]] -== Expand All -// Expand(All) - -Given a start node, and depending on the pattern relationship, the `Expand(All)` operator will traverse incoming or outgoing relationships. - - -.Expand(All) -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof) -RETURN fof ----- - -.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 | fof | 1 | 2 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 1 | 2 | 5 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 1.137 | Fused in Pipeline 0 | -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 7, total allocated memory: 184 ----- - -====== - - -[[query-plan-expand-into]] -== Expand Into -// Expand(Into) +[[query-plan-cartesian-product]] +=== Cartesian Product -When both the start and end node have already been found, the `Expand(Into)` operator is used to find all relationships connecting the two nodes. -As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. -This can make a noticeable difference when dense nodes appear as end points. +The `CartesianProduct` operator produces a cartesian product of the two inputs -- each row coming from the left child operator will be combined with all the rows from the right child operator. +`CartesianProduct` generally exhibits bad performance and ought to be avoided if possible. -.Expand(Into) +.CartesianProduct ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof)-->(p) -RETURN fof +MATCH + (p:Person), + (t:Team) +RETURN p, t ---- .Query Plan @@ -3640,98 +3377,55 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | fof | 0 | 0 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | not anon_1 = anon_0 | 0 | 0 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(Into) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 0 | 0 | 6 | 896 | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (p)<-[anon_1]-(fof) | 1 | 1 | 5 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 0.546 | 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: 13, total allocated memory: 976 +Total database accesses: 142, total allocated memory: 1816 ---- ====== -[[query-plan-optional-expand-all]] -== Optional Expand All -// OptionalExpand(All) - -The `OptionalExpand(All)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-all[Expand(All)], apart from when no relationships match the direction, type and property predicates. -In this situation, `OptionalExpand(all)` will return a single row with the relationship and end node set to `null`. - - -.OptionalExpand(All) -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (p:Person) -OPTIONAL MATCH (p)-[works_in:WORKS_IN]->(l) - WHERE works_in.duration > 180 -RETURN p, l ----- - -.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, l | 14 | 15 | 1 | | | | | -| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +OptionalExpand(All) | (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > $autoint_0 | 14 | 15 | 53 | | | | | -| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 5/0 | 1,233 | Fused in Pipeline 0 | -+----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 125, total allocated memory: 184 ----- +[[execution-plans-operators-hash-join-general]] +=== Hash joins in general -====== +Hash joins have two inputs: the build input and probe input. +The query planner assigns these roles so that the smaller of the two inputs is the build input. +The build input is pulled in eagerly, and is used to build a probe table. +Once this is complete, the probe table is checked for each row coming from the probe input side. +In query plans, the build input is always the left operator, and the probe input the right operator. -[[query-plan-optional-expand-into]] -== Optional Expand Into -// OptionalExpand(Into) +[[query-plan-node-hash-join]] +=== Node Hash Join -The `OptionalExpand(Into)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-into[Expand(Into)], apart from when no matching relationships are found. -In this situation, `OptionalExpand(Into)` will return a single row with the relationship and end node set to `null`. -As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. -This can make a noticeable difference when dense nodes appear as end points. +The `NodeHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. +`NodeHashJoin` executes the hash join on node ids. +As primitive types and arrays can be used, it can be done very efficiently. -.OptionalExpand(Into) +.NodeHashJoin ====== - .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[works_in:WORKS_IN]->(l) -OPTIONAL MATCH (l)-->(p) -RETURN p +MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattias'}) +USING JOIN ON loc +RETURN loc.name ---- .Query Plan @@ -3741,43 +3435,55 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 15 | 15 | 0 | | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +OptionalExpand(Into) | (l)-[anon_0]->(p) | 15 | 15 | 105 | 3360 | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (p)-[works_in:WORKS_IN]->(l) | 15 | 15 | 39 | | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 7/0 | 3,925 | Fused in Pipeline 0 | -+-----------------------+--- --------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | loc.name AS `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Filter | not anon_0 = anon_1 | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +NodeHashJoin | loc | 10 | 0 | 0 | 3688 | | 0.053 | In Pipeline 2 | +| |\ +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Expand(All) | (matt)-[anon_1:WORKS_IN]->(loc) | 19 | 0 | 0 | | | | | +| | | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeek | RANGE INDEX matt:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 120 | 1/0 | 0.288 | Fused in Pipeline 1 | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Expand(All) | (bob)-[anon_0:WORKS_IN]->(loc) | 19 | 1 | 4 | | | | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX bob:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 3/0 | 0.556 | Fused in Pipeline 0 | ++------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 215, total allocated memory: 3440 +Total database accesses: 7, total allocated memory: 3888 ---- ====== -[[query-plan-varlength-expand-all]] -== VarLength Expand All -// VarLengthExpand(All) +[[query-plan-value-hash-join]] +=== Value Hash Join -Given a start node, the `VarLengthExpand(All)` operator will traverse variable-length and quantified relationships. +The `ValueHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. +This operator allows for arbitrary values to be used as the join key. +It is most frequently used to solve predicates of the form: `n.prop1 = m.prop2` (i.e. equality predicates between two property columns). -.VarLengthExpand(All) +.ValueHashJoin ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(q:Person) +MATCH + (p:Person), + (q:Person) +WHERE p.age = q.age RETURN p, q ---- @@ -3788,44 +3494,50 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 40 | 48 | 0 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | q:Person | 40 | 48 | 96 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +VarLengthExpand(All) | (p)-[anon_0:FRIENDS_WITH*..2]-(q) | 40 | 48 | 151 | 128 | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 10,457 | 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: 318, total allocated memory: 208 +Total database accesses: 71, total allocated memory: 664 ---- ====== -[[query-plan-varlength-expand-into]] -== VarLength Expand Into -// VarLengthExpand(Into) +[[query-plan-node-left-right-outer-hash-join]] +=== Node Left/Right Outer Hash Join -When both the start and end node have already been found, the `VarLengthExpand(Into)` operator is used to find all variable-length and quantified relationships connecting the two nodes. +The `NodeLeftOuterHashJoin` and `NodeRightOuterHashJoin` operators are variations of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. +The query below can be planned with either a left or a right outer join. +The decision depends on the cardinalities of the left-hand and right-hand sides; i.e. how many rows would be returned, respectively, for `(a:Person)` and `(a)-->(b:Person)`. +If `(a:Person)` returns fewer results than `(a)-->(b:Person)`, a left outer join -- indicated by `NodeLeftOuterHashJoin` -- is planned. +On the other hand, if `(a:Person)` returns more results than `(a)-->(b:Person)`, a right outer join -- indicated by `NodeRightOuterHashJoin` -- is planned instead. -.VarLengthExpand(Into) + +.NodeRightOuterHashJoin ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(p:Person) -RETURN p +MATCH (a:Person) +OPTIONAL MATCH (a)-->(b:Person) +USING JOIN ON a +RETURN a.name, b.name ---- .Query Plan @@ -3835,105 +3547,62 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 3 | 4 | 0 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +VarLengthExpand(Into) | (p)-[anon_0:FRIENDS_WITH*..2]-(p) | 3 | 4 | 151 | 128 | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 0,797 | 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: 222, total allocated memory: 192 +Total database accesses: 211, total allocated memory: 4312 ---- ====== -[[query-plan-varlength-expand-pruning]] -== VarLength Expand Pruning -// VarLengthExpand(Pruning) +[[traversal-operators]] +== Traversal operators -Given a start node, the `VarLengthExpand(Pruning)` operator will traverse variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. -However, as an optimization, some paths will not be explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). +Traversal operators enable complex graph traversals by defining how nodes and relationships are connected in a given pattern. +They allow Cypher to express and match various pattern types, such as xref:patterns/fixed-length-patterns.adoc[fixed-length patterns], xref:patterns/variable-length-patterns.adoc[variable-length patterns], xref:patterns/shortest-paths.adoc[shortest paths], and xref:patterns/non-linear-patterns.adoc[non-linear patterns]. -This kind of expand is only planned when: -* The individual paths are not of interest. -* The relationships have an upper bound. - -The `VarLengthExpand(Pruning)` operator guarantees that all the end nodes produced will be unique. - - -.VarLengthExpand(Pruning) -====== -.Query -[source, cypher] ----- -PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *3..4]-(q:Person) -RETURN DISTINCT p, q ----- - -.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) | 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: 30, total allocated memory: 480 ----- - -====== - - -[[query-plan-breadth-first-varlength-expand-pruning-bfs-all]] -== Breadth First VarLength Expand Pruning -// VarLengthExpand(Pruning,BFS) -// New in 5.0 - -Given a start node, the `VarLengthExpand(Pruning,BFS,All)` operator traverses variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. -However, as an optimization, it instead performs a breadth-first search (BFS) and while expanding, some paths are not explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). -This is only used in cases where the individual paths are not of interest. +[[query-plan-anti]] +=== Anti -This kind of expand is only planned when: +The `Anti` operator tests for the absence of a pattern. +If there are incoming rows, the `Anti` operator will yield no rows. +If there are no incoming rows, the `Anti` operator will yield a single row. -* The individual paths are not of interest. -* The lower bound is either `0` or `1` (default). -This operator guarantees that all the end nodes produced are unique. +.Anti +====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *..4]-(q:Person) -RETURN DISTINCT p, q +CYPHER runtime=pipelined +MATCH + (me:Person {name: 'me'}), + (other:Person) +WHERE NOT (me)-[:FRIENDS_WITH]->(other) +RETURN other.name ---- .Query Plan @@ -3943,46 +3612,58 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| 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 | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +OrderedDistinct | 1 | p, q | 12 | 0 | 0 | 40 | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +Filter | 2 | q:Person | 13 | 0 | 0 | | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +VarLengthExpand(Pruning,BFS,All) | 3 | (p)-[:FRIENDS_WITH*..4]-(q) | 13 | 0 | 38 | 952 | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +NodeByLabelScan | 4 | p:Person | 10 | 10 | 11 | 248 | 3/0 | 4.662 | p ASC | Fused in Pipeline 0 | -+-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | `other.name` | 4 | 12 | 0 | | 0/0 | 0.068 | | +| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | other.name AS `other.name` | 4 | 12 | 24 | | 2/0 | 0.111 | In Pipeline 4 | +| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Apply | | 4 | 12 | 0 | | 0/0 | | | +| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Anti | | 4 | 12 | 0 | 1256 | 0/0 | 0.084 | In Pipeline 4 | +| | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Limit | 1 | 11 | 2 | 0 | 752 | | | | +| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(Into) | (me)-[anon_2:FRIENDS_WITH]->(other) | 1 | 2 | 81 | 2632 | | | | +| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | me, other | 14 | 14 | 0 | 3192 | 1/0 | 0.904 | Fused in Pipeline 3 | +| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +CartesianProduct | | 14 | 14 | 0 | 3672 | | 1.466 | In Pipeline 2 | +| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +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 | ++-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 49, total allocated memory: 1200 +Total database accesses: 178, total allocated memory: 6744 ---- -[role=label--new-5.9] -[[query-plan-repeat]] -== Repeat (Trail) -// Repeat(Trail) +====== -Given a start node, the `Repeat(Trail)` operator will traverse xref::patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path patterns] that cannot be solved (or solved efficiently) with the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. -Similar to an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator, it takes a single row from the left-hand side and applies the operators on the right-hand side. -In contrast to `Apply`, however, it repeatedly applies these operators in accordance with the quantifiers on the quantified path pattern. -In the following example, the operator will repeat twice and produce rows for both repetitions. -.Repeat(Trail) +[[query-plan-optional]] +=== Optional + +The `Optional` operator is used to solve some xref::clauses/optional-match.adoc[OPTIONAL MATCH] queries. +It will pull data from its source, simply passing it through if any data exists. +However, if no data is returned by its source, `Optional` will yield a single row with all columns set to `null`. + + +.Optional ====== .Query [source, cypher] ---- PROFILE -MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) -RETURN me, friend +MATCH (p:Person {name: 'me'}) +OPTIONAL MATCH (q:Person {name: 'Lulu'}) +RETURN p, q ---- .Query Plan @@ -3992,57 +3673,44 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 29792 | 0/0 | 1.696 | Fused in Pipeline 2 | -| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | -| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 7 | a | 15 | 34 | 0 | 15672 | 2/0 | 3.245 | Fused in Pipeline 1 | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 0.107 | In Pipeline 0 | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | p, q | 1 | 1 | 0 | | 2/0 | 0.079 | In Pipeline 2 | +| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +Apply | | 1 | 1 | 0 | | 0/0 | 0.096 | | +| |\ +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| | +Optional | p | 1 | 1 | 0 | 768 | 0/0 | 0.043 | In Pipeline 2 | +| | | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 2152 | 1/0 | 0.098 | In Pipeline 1 | +| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.364 | In Pipeline 0 | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 747, total allocated memory: 45832 +Total database accesses: 3, total allocated memory: 3000 ---- ====== -[role=label--new-5.9] -[[query-plan-nullify-metadata]] -== Nullify Metadata -// NullifyMetadata +[[query-plan-expand-all]] +=== Expand All -`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)`. +Given a start node, and depending on the pattern relationship, the `Expand(All)` operator will traverse incoming or outgoing relationships. -.NullifyMetadata + +.Expand(All) ====== .Query [source, cypher] ---- PROFILE -MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) -RETURN me, friend +MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof) +RETURN fof ---- .Query Plan @@ -4052,58 +3720,43 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 29792 | 0/0 | 1.696 | Fused in Pipeline 2 | -| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | -| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 7 | a | 15 | 34 | 0 | 15672 | 2/0 | 3.245 | Fused in Pipeline 1 | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 0.107 | In Pipeline 0 | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | fof | 1 | 2 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 1 | 2 | 5 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 1.137 | Fused in Pipeline 0 | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 747, total allocated memory: 45832 +Total database accesses: 7, total allocated memory: 184 ---- ====== -[[query-plan-assert-same-node]] -== Assert Same Node -// AssertSameNode -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. +[[query-plan-expand-into]] +=== Expand Into +When both the start and end node have already been found, the `Expand(Into)` operator is used to find all relationships connecting the two nodes. +As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. +This can make a noticeable difference when dense nodes appear as end points. -.AssertSameNode + +.Expand(Into) ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -MERGE (t:Team {name: 'Engineering', id: 42}) +MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof)-->(p) +RETURN fof ---- .Query Plan @@ -4111,52 +3764,50 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- Planner COST -Runtime SLOTTED +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} -+---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | | 1 | 0 | 0 | 0/0 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +EmptyResult | | 1 | 0 | 0 | 0/0 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | 0/0 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +AssertSameNode | t | 0 | 1 | 0 | 0/0 | -| |\ +-------------------------------------------------------+----------------+------+---------+------------------------+ -| | +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(id) WHERE id = $autoint_1 | 1 | 1 | 1 | 0/1 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 1 | 1 | 0/1 | -+---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ +Batch size 128 -Total database accesses: 2, total allocated memory: 64 ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | fof | 0 | 0 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | not anon_1 = anon_0 | 0 | 0 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(Into) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 0 | 0 | 6 | 896 | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (p)<-[anon_1]-(fof) | 1 | 1 | 5 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 0.546 | Fused in Pipeline 0 | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 13, total allocated memory: 976 ---- ====== -[role=label--new-5.8] -[[query-plan-assert-same-relationship]] -== Assert Same Relationship -// AssertSameRelationship +[[query-plan-optional-expand-all]] +=== Optional Expand All -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 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. +The `OptionalExpand(All)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-all[Expand(All)], apart from when no relationships match the direction, type and property predicates. +In this situation, `OptionalExpand(all)` will return a single row with the relationship and end node set to `null`. -.AssertSameRelationship +.OptionalExpand(All) ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -MERGE (person)-[work:WORKS_IN {id: 0, badgeNumber: 4332}]->(location) +MATCH (p:Person) +OPTIONAL MATCH (p)-[works_in:WORKS_IN]->(l) + WHERE works_in.duration > 180 +RETURN p, l ---- .Query Plan @@ -4164,51 +3815,47 @@ MERGE (person)-[work:WORKS_IN {id: 0, badgeNumber: 4332}]->(location) ---- Planner COST -Runtime SLOTTED +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} -+-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| 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 | -+-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +Batch size 128 -Total database accesses: 2, total allocated memory: 64 ++----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, l | 14 | 15 | 1 | | | | | +| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +OptionalExpand(All) | (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > $autoint_0 | 14 | 15 | 53 | | | | | +| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 5/0 | 1,233 | Fused in Pipeline 0 | ++----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 125, total allocated memory: 184 ---- ====== -// DropResult -- removed in 4.3 - - -[[query-plan-empty-result]] -== Empty Result -// EmptyResult +[[query-plan-optional-expand-into]] +=== Optional Expand Into -The `EmptyResult` operator eagerly loads all incoming data and discards it. +The `OptionalExpand(Into)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-into[Expand(Into)], apart from when no matching relationships are found. +In this situation, `OptionalExpand(Into)` will return a single row with the relationship and end node set to `null`. +As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. +This can make a noticeable difference when dense nodes appear as end points. -.EmptyResult +.OptionalExpand(Into) ====== .Query [source, cypher] ---- PROFILE -CREATE (:Person) +MATCH (p:Person)-[works_in:WORKS_IN]->(l) +OPTIONAL MATCH (l)-->(p) +RETURN p ---- .Query Plan @@ -4218,43 +3865,43 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | | 1 | 0 | 0 | | | | -| | +-----------------+----------------+------+---------+ | | | -| +EmptyResult | | 1 | 0 | 0 | | | | -| | +-----------------+----------------+------+---------+ | | | -| +Create | (anon_0:Person) | 1 | 1 | 1 | 0/0 | 0.000 | Fused in Pipeline 0 | -+-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ ++-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p | 15 | 15 | 0 | | | | | +| | +------------------------------+----------------+------+---------+----------------+ | | | +| +OptionalExpand(Into) | (l)-[anon_0]->(p) | 15 | 15 | 105 | 3360 | | | | +| | +------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (p)-[works_in:WORKS_IN]->(l) | 15 | 15 | 39 | | | | | +| | +------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 7/0 | 3,925 | Fused in Pipeline 0 | ++-----------------------+--- --------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 215, total allocated memory: 3440 ---- ====== -[[query-plan-produce-results]] -== Produce Results -// ProduceResults +[[query-plan-varlength-expand-all]] +=== VarLength Expand All -The `ProduceResults` operator prepares the result so that it is consumable by the user, such as transforming internal values to user values. -It is present in every single query that returns data to the user, and has little bearing on performance optimisation. +Given a start node, the `VarLengthExpand(All)` operator will traverse variable-length and quantified relationships. -.ProduceResults +.VarLengthExpand(All) ====== .Query [source, cypher] ---- PROFILE -MATCH (n) -RETURN n +MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(q:Person) +RETURN p, q ---- .Query Plan @@ -4264,41 +3911,43 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | n | 35 | 35 | 0 | | | | | -| | +---------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | n | 35 | 35 | 36 | 120 | 3/0 | 0.508 | Fused in Pipeline 0 | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, q | 40 | 48 | 0 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | q:Person | 40 | 48 | 96 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +VarLengthExpand(All) | (p)-[anon_0:FRIENDS_WITH*..2]-(q) | 40 | 48 | 151 | 128 | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 10,457 | Fused in Pipeline 0 | ++-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 36, total allocated memory: 184 +Total database accesses: 318, total allocated memory: 208 ---- ====== -[[query-plan-load-csv]] -== Load CSV -// LoadCSV +[[query-plan-varlength-expand-into]] +=== VarLength Expand Into -The `LoadCSV` operator loads data from a CSV source into the query. -It is used whenever the xref::clauses/load-csv.adoc[LOAD CSV] clause is used in a query. +When both the start and end node have already been found, the `VarLengthExpand(Into)` operator is used to find all variable-length and quantified relationships connecting the two nodes. -.LoadCSV +.VarLengthExpand(Into) ====== .Query [source, cypher] ---- PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -RETURN line +MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(p:Person) +RETURN p ---- .Query Plan @@ -4308,60 +3957,50 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | line | 10 | 4 | 0 | | 0/0 | 0.210 | | -| | +---------+----------------+------+---------+----------------+------------------------+-----------+ | -| +LoadCSV | line | 10 | 4 | 0 | 72 | | | In Pipeline 1 | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p | 3 | 4 | 0 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +VarLengthExpand(Into) | (p)-[anon_0:FRIENDS_WITH*..2]-(p) | 3 | 4 | 151 | 128 | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 0,797 | Fused in Pipeline 0 | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 184 + +Total database accesses: 222, total allocated memory: 192 ---- ====== -[[execution-plans-operators-hash-join-general]] -== Hash joins in general - -Hash joins have two inputs: the build input and probe input. -The query planner assigns these roles so that the smaller of the two inputs is the build input. -The build input is pulled in eagerly, and is used to build a probe table. -Once this is complete, the probe table is checked for each row coming from the probe input side. - -In query plans, the build input is always the left operator, and the probe input the right operator. - -There are four hash join operators: +[[query-plan-varlength-expand-pruning]] +=== VarLength Expand Pruning +// VarLengthExpand(Pruning) -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-hash-join[NodeHashJoin] -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-value-hash-join[ValueHashJoin] -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-left-right-outer-hash-join[NodeLeftOuterHashJoin] -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-left-right-outer-hash-join[NodeRightOuterHashJoin] +Given a start node, the `VarLengthExpand(Pruning)` operator will traverse variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. +However, as an optimization, some paths will not be explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). +This kind of expand is only planned when: -[[query-plan-node-hash-join]] -== Node Hash Join -// NodeHashJoin +* The individual paths are not of interest. +* The relationships have an upper bound. -The `NodeHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. -`NodeHashJoin` executes the hash join on node ids. -As primitive types and arrays can be used, it can be done very efficiently. +The `VarLengthExpand(Pruning)` operator guarantees that all the end nodes produced will be unique. -.NodeHashJoin +.VarLengthExpand(Pruning) ====== .Query [source, cypher] ---- PROFILE -MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattias'}) -USING JOIN ON loc -RETURN loc.name +MATCH (p:Person)-[:FRIENDS_WITH *3..4]-(q:Person) +RETURN DISTINCT p, q ---- .Query Plan @@ -4371,57 +4010,52 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | loc.name AS `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Filter | not anon_0 = anon_1 | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +NodeHashJoin | loc | 10 | 0 | 0 | 3688 | | 0.053 | In Pipeline 2 | -| |\ +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Expand(All) | (matt)-[anon_1:WORKS_IN]->(loc) | 19 | 0 | 0 | | | | | -| | | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +NodeIndexSeek | RANGE INDEX matt:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 120 | 1/0 | 0.288 | Fused in Pipeline 1 | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Expand(All) | (bob)-[anon_0:WORKS_IN]->(loc) | 19 | 1 | 4 | | | | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX bob:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 3/0 | 0.556 | 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 | 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: 7, total allocated memory: 3888 +Total database accesses: 30, total allocated memory: 480 ---- ====== -[[query-plan-value-hash-join]] -== Value Hash Join -// ValueHashJoin +[[query-plan-breadth-first-varlength-expand-pruning-bfs-all]] +=== Breadth First VarLength Expand Pruning +// VarLengthExpand(Pruning,BFS) +// New in 5.0 -The `ValueHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. -This operator allows for arbitrary values to be used as the join key. -It is most frequently used to solve predicates of the form: `n.prop1 = m.prop2` (i.e. equality predicates between two property columns). +Given a start node, the `VarLengthExpand(Pruning,BFS,All)` operator traverses variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. +However, as an optimization, it instead performs a breadth-first search (BFS) and while expanding, some paths are not explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). +This is only used in cases where the individual paths are not of interest. +This kind of expand is only planned when: -.ValueHashJoin -====== +* The individual paths are not of interest. +* The lower bound is either `0` or `1` (default). + +This operator guarantees that all the end nodes produced are unique. .Query [source, cypher] ---- PROFILE -MATCH - (p:Person), - (q:Person) -WHERE p.age = q.age -RETURN p, q +MATCH (p:Person)-[:FRIENDS_WITH *..4]-(q:Person) +RETURN DISTINCT p, q ---- .Query Plan @@ -4431,108 +4065,105 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -| |\ +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +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 | -+-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| 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 | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +OrderedDistinct | 1 | p, q | 12 | 0 | 0 | 40 | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +Filter | 2 | q:Person | 13 | 0 | 0 | | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +VarLengthExpand(Pruning,BFS,All) | 3 | (p)-[:FRIENDS_WITH*..4]-(q) | 13 | 0 | 38 | 952 | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +NodeByLabelScan | 4 | p:Person | 10 | 10 | 11 | 248 | 3/0 | 4.662 | p ASC | Fused in Pipeline 0 | ++-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 71, total allocated memory: 664 +Total database accesses: 49, total allocated memory: 1200 ---- -====== - - -[[query-plan-node-left-right-outer-hash-join]] -== Node Left/Right Outer Hash Join -// NodeLeftOuterHashJoin -// NodeRightOuterHashJoin - -The `NodeLeftOuterHashJoin` and `NodeRightOuterHashJoin` operators are variations of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. -The query below can be planned with either a left or a right outer join. -The decision depends on the cardinalities of the left-hand and right-hand sides; i.e. how many rows would be returned, respectively, for `(a:Person)` and `(a)-->(b:Person)`. -If `(a:Person)` returns fewer results than `(a)-->(b:Person)`, a left outer join -- indicated by `NodeLeftOuterHashJoin` -- is planned. -On the other hand, if `(a:Person)` returns more results than `(a)-->(b:Person)`, a right outer join -- indicated by `NodeRightOuterHashJoin` -- is planned instead. +[role=label--new-5.9] +[[query-plan-repeat]] +=== Repeat (Trail) +// Repeat(Trail) +Given a start node, the `Repeat(Trail)` operator will traverse xref::patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path patterns] that cannot be solved (or solved efficiently) with the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. +Similar to an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator, it takes a single row from the left-hand side and applies the operators on the right-hand side. +In contrast to `Apply`, however, it repeatedly applies these operators in accordance with the quantifiers on the quantified path pattern. +In the following example, the operator will repeat twice and produce rows for both repetitions. -.NodeRightOuterHashJoin +.Repeat(Trail) ====== .Query [source, cypher] ---- PROFILE -MATCH (a:Person) -OPTIONAL MATCH (a)-->(b:Person) -USING JOIN ON a -RETURN a.name, b.name ----- - -.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 | `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 | -+-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) +RETURN me, friend +---- -Total database accesses: 211, total allocated memory: 4312 +.Query Plan +[role="queryplan", subs="attributes+"] ---- +Planner COST -====== +Runtime PIPELINED +Runtime version {neo4j-version} -[[query-plan-triadic-selection]] -== Triadic Selection -// TriadicSelection +Batch size 128 -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. ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 30112 | 0/0 | 4.943 | Fused in Pipeline 2 | +| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | +| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 7 | a | 15 | 34 | 0 | 15992 | 2/0 | 5.253 | Fused in Pipeline 1 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 1.130 | In Pipeline 0 | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +Total database accesses: 747, total allocated memory: 45832 +---- -.TriadicSelection +====== + +[role=label--new-5.9] +[[query-plan-nullify-metadata]] +=== Nullify Metadata + +`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 ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) -WHERE NOT (me)-[:FRIENDS_WITH]-(other) -RETURN other.name +MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) +RETURN me, friend ---- .Query Plan @@ -4540,58 +4171,61 @@ RETURN other.name ---- Planner COST -Runtime SLOTTED +Runtime PIPELINED -Runtime version {neo4j-version-minor} - -+-------------------+----------------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+-------------------+----------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | `other.name` | 4 | 24 | 0 | 0/0 | -| | +----------------------------------------+----------------+------+---------+------------------------+ -| +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 | -| |\ +----------------------------------------+----------------+------+---------+------------------------+ -| | | +----------------------------------------+----------------+------+---------+------------------------+ -| | +Expand(All) | (anon_3)-[anon_4:FRIENDS_WITH]-(other) | 16 | 48 | 98 | 48/0 | -| | | +----------------------------------------+----------------+------+---------+------------------------+ -| | +Argument | anon_3, anon_2 | 24 | 24 | 0 | 0/0 | -| | +----------------------------------------+----------------+------+---------+------------------------+ -| +Expand(All) | (me)-[anon_2:FRIENDS_WITH]-(anon_3) | 24 | 24 | 53 | 28/0 | -| | +----------------------------------------+----------------+------+---------+------------------------+ -| +NodeByLabelScan | me:Person | 15 | 15 | 16 | 1/0 | -+-------------------+----------------------------------------+----------------+------+---------+------------------------+ +Runtime version {neo4j-version} -Total database accesses: 246, total allocated memory: 64 ----- +Batch size 128 -====== ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 30112 | 0/0 | 4.824 | Fused in Pipeline 2 | +| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | +| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 7 | a | 15 | 34 | 0 | 15992 | 2/0 | 5.307 | Fused in Pipeline 1 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 0.183 | In Pipeline 0 | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +Total database accesses: 747, total allocated memory: 45832 +---- -[[query-plan-triadic-build]] -== Triadic Build -// TriadicBuild +====== -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. +[[query-plan-shortest-path]] +=== Shortest path +// ShortestPath +The `ShortestPath` operator finds one or all shortest paths between two previously matched node variables. +This operator is used for the xref:patterns/reference.adoc#shortest-functions[`shortestPath()` and `allShortestPaths`] functions. -.TriadicBuild +.ShortestPath ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=pipelined -MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) -WHERE NOT (me)-[:FRIENDS_WITH]-(other) -RETURN other.name +MATCH + (andy:Person {name: 'Andy'}), + (mattias:Person {name: 'Mattias'}), + p = shortestPath((andy)-[*]-(mattias)) +RETURN p ---- .Query Plan @@ -4601,62 +4235,54 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +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 | | | -| |\ +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | | +----------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | (anon_3)-[anon_4:FRIENDS_WITH]-(other) | 16 | 48 | 98 | | | | | -| | | +----------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | anon_3, anon_2 | 24 | 24 | 0 | 4200 | 0/0 | 0.397 | Fused in Pipeline 2 | -| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +TriadicBuild | (me)--(anon_3) | 24 | 24 | 0 | 888 | 0/0 | 1.427 | In Pipeline 1 | -| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Expand(All) | (me)-[anon_2:FRIENDS_WITH]-(anon_3) | 24 | 24 | 39 | | | | | -| | +----------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan| me:Person | 15 | 15 | 16 | 120 | 3/0 | 0,200 | Fused in Pipeline 0 | -+-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | p | 1 | 1 | 0 | | 1/0 | 0.241 | | +| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +ShortestPath | p = (andy)-[anon_0*]-(mattias) | 1 | 1 | 1 | 1424 | | | In Pipeline 1 | +| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +MultiNodeIndexSeek | RANGE INDEX andy:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 120 | 1/1 | 0.308 | In Pipeline 0 | +| | RANGE INDEX mattias:Person(name) WHERE name = $autostring_1 | | | | | | | | ++---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 256, total allocated memory: 7376 +Total database accesses: 5, total allocated memory: 1488 ---- ====== +[role=label--new-5.21] +[[query-plan-stateful-shortest-path-into]] +=== StatefulShortestPath(Into) +// StatefulShortestPath(Into) -[[query-plan-triadic-filter]] -== Triadic Filter -// TriadicFilter - -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. +//// +[source, cypher, role=test-setup] +---- +DROP INDEX range_person_name IF EXISTS; +CREATE CONSTRAINT person_name_unique IF NOT EXISTS FOR (p:Person) REQUIRE (p.name) IS UNIQUE; +---- +//// +The `StatefulShortestPath(Into)` operator finds shortest paths between a start node and a single target node. +It uses a bidirectional breadth-first search (BFS) algorithm, which performs two BFS invocations at the same time, one from the left boundary node and one from the right boundary node. +Once a node is found by both BFS invocations, which indicates that it can be reached from both boundary nodes, the algorithm successfully terminates. +If one of the BFS invocations exhausts its search before intersecting, either because no further nodes can be reached or because the maximum number of hops has been reached, then there is no valid path between the boundary nodes and the algorithm terminates. -.TriadicFilter +.StatefulShortestPath(Into) ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=pipelined -MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) -WHERE NOT (me)-[:FRIENDS_WITH]-(other) -RETURN other.name +MATCH + p = ALL SHORTEST (chris:Person {name: 'Chris'})(()-[]-()-[]-()){1,}(stefan:Person {name: 'Stefan'}) +RETURN p ---- .Query Plan @@ -4666,50 +4292,39 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +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 | | | -| |\ +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | | +----------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | (anon_3)-[anon_4:FRIENDS_WITH]-(other) | 16 | 48 | 98 | | | | | -| | | +----------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | anon_3, anon_2 | 24 | 24 | 0 | 4200 | 0/0 | 0.496 | Fused in Pipeline 2 | -| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +TriadicBuild | (me)--(anon_3) | 24 | 24 | 0 | 888 | 0/0 | 3.268 | In Pipeline 1 | -| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Expand(All) | (me)-[anon_2:FRIENDS_WITH]-(anon_3) | 24 | 24 | 39 | | | | | -| | +----------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan| me:Person | 15 | 15 | 16 | 120 | 3/0 | 0,481 | Fused in Pipeline 0 | -+-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | p | 2 | 2 | 0 | 0 | 0/0 | 0.039 | | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | (chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (stefan) AS p | 2 | 2 | 0 | | 0/0 | 1.365 | | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(Into) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (stefan) | 2 | 2 | 39 | 22237 | 1/0 | 37.376 | In Pipeline 1 | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +MultiNodeIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 376 | 1/1 | 10.245 | In Pipeline 0 | +| | | UNIQUE stefan:Person(name) WHERE name = $autostring_1 | | | | | | | | ++-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ + +Total database accesses: 43, total allocated memory: 22557 -Total database accesses: 256, total allocated memory: 7376 ---- ====== +[role=label--new-5.21] +[[query-plan-stateful-shortest-path-all]] +=== StatefulShortestPath(All) +// StatefulShortestPath(All) -[[query-plan-cartesian-product]] -== Cartesian Product -// CartesianProduct - -The `CartesianProduct` operator produces a cartesian product of the two inputs -- each row coming from the left child operator will be combined with all the rows from the right child operator. -`CartesianProduct` generally exhibits bad performance and ought to be avoided if possible. +The `StatefulShortestPath(All)` operator finds shortest paths from a single node to multiple target nodes. +It uses a breadth-first search algorithm. -.CartesianProduct +.StatefulShortestPath(All) ====== .Query @@ -4717,9 +4332,8 @@ The `CartesianProduct` operator produces a cartesian product of the two inputs - ---- PROFILE MATCH - (p:Person), - (t:Team) -RETURN p, t + p = ALL SHORTEST (chris:Person {name:'Chris'})(()-[]-()-[]-()){1,}(location:Location) +RETURN length(p) AS pathLength, location.name AS locationName ---- .Query Plan @@ -4729,45 +4343,58 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -| |\ +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +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 | -+--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | pathLength, locationName | 14 | 20 | 0 | 0 | 0/0 | 0.074 | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | length((chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (location)) AS pathLength, | 14 | 20 | 40 | | 1/0 | 6.828 | | +| | | | location.name AS locationName | | | | | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(All) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (location) | 14 | 20 | 179 | 37663 | 1/0 | 52.849 | In Pipeline 1 | +| | | | expanding from: chris | | | | | | | | +| | | | inlined predicates: location:Location | | | | | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +NodeUniqueIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 0/1 | 9.078 | In Pipeline 0 | ++----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ + +Total database accesses: 221, total allocated memory: 37983 -Total database accesses: 142, total allocated memory: 1816 ---- +//// +[source, cypher, role=test-setup] +---- +DROP CONSTRAINT person_name_unique IF EXISTS; +CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name); +---- +//// ====== -[[query-plan-foreach]] -== Foreach -// Foreach +[[query-plan-triadic-selection]] +=== Triadic Selection -The `Foreach` operator executes a nested loop between the left child operator and the right child operator. -In an analogous manner to the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator, it takes a row from the left-hand side and, using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[Argument] operator, provides it to the operator tree on the right-hand side. -`Foreach` will yield all the rows coming in from the left-hand side; all results from the right-hand side are pulled in and discarded. +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. -.Foreach +.TriadicSelection ====== .Query [source, cypher] ---- PROFILE -FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) +CYPHER runtime=slotted +MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) +WHERE NOT (me)-[:FRIENDS_WITH]-(other) +RETURN other.name ---- .Query Plan @@ -4777,44 +4404,54 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} + ++-------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | ++-------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| +ProduceResults | 0 | `other.name` | 15 | 24 | 0 | 0 | 0/0 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| +Projection | 1 | other.name AS `other.name` | 15 | 24 | 24 | | 0/0 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| +Filter | 2 | NOT anon_2 = anon_0 | 15 | 24 | 0 | | 0/0 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| +TriadicSelection | 3 | WHERE NOT (me)--(other) | 15 | 48 | 0 | | 0/0 | +| |\ +----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| | +Expand(All) | 4 | (anon_1)-[anon_2:FRIENDS_WITH]-(other) | 16 | 48 | 72 | | 0/0 | +| | | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| | +Argument | 5 | anon_1, anon_0 | 24 | 24 | 0 | | 0/0 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| +Expand(All) | 6 | (me)-[anon_0:FRIENDS_WITH]-(anon_1) | 24 | 24 | 38 | | 2/0 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+ +| +NodeByLabelScan | 7 | me:Person | 14 | 14 | 15 | | 1/0 | ++-------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+ -+-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ -| 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], CREATE (anon_0:Person {age: value}) | 1 | 1 | 9 | 0/0 | -+-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ - -Total database accesses: 9, total allocated memory: 64 +Total database accesses: 246, total allocated memory: 64 ---- ====== -[[query-plan-transaction-foreach]] -== TransactionForeach -// TransactionForeach -`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. +[[query-plan-triadic-build]] +=== Triadic Build -.TransactionForeach -====== +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. -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + +.TriadicBuild +====== .Query [source, cypher] ---- -PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -CALL (line) { - CREATE (a: Artist {name: line[0]}) -} IN TRANSACTIONS OF 100 ROWS +PROFILE +CYPHER runtime=pipelined +MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) +WHERE NOT (me)-[:FRIENDS_WITH]-(other) +RETURN other.name ---- .Query Plan @@ -4824,52 +4461,60 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | - +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `other.name` | 15 | 24 | 0 | 0 | 0/0 | 0.172 | | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | other.name AS `other.name` | 15 | 24 | 48 | | 0/0 | 0.162 | | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Filter | 2 | NOT anon_2 = anon_0 | 15 | 24 | 0 | | 0/0 | 0.134 | | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +TriadicFilter | 10 | WHERE NOT (me)--(other) | 15 | 48 | 0 | 7216 | 0/0 | 0.251 | In Pipeline 3 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Apply | 9 | | 16 | 48 | 0 | | 0/0 | | | +| |\ +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Expand(All) | 4 | (anon_1)-[anon_2:FRIENDS_WITH]-(other) | 16 | 48 | 72 | | | | | +| | | +----+----------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 5 | anon_1, anon_0 | 24 | 24 | 0 | 4464 | 0/0 | 0.384 | Fused in Pipeline 2 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +TriadicBuild | 8 | (me)--(anon_1) | 24 | 24 | 0 | 1080 | 0/0 | 1.670 | In Pipeline 1 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Expand(All) | 6 | (me)-[anon_0:FRIENDS_WITH]-(anon_1) | 24 | 24 | 38 | | | | | +| | +----+----------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 7 | me:Person | 14 | 14 | 15 | 376 | 3/0 | 0.361 | Fused in Pipeline 0 | ++------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - Total database accesses: 12, total allocated memory: 5704 +Total database accesses: 256, total allocated memory: 7376 ---- ====== -[[query-plan-subquery-foreach]] -== SubqueryForeach -// SubqueryForeach -`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. +[[query-plan-triadic-filter]] +=== Triadic Filter -.SubqueryForeach -====== +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. -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + +.TriadicFilter +====== .Query [source, cypher] ---- PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -CALL (line) { - CREATE (a: Artist {name: line[0]}) -} +CYPHER runtime=pipelined +MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) +WHERE NOT (me)-[:FRIENDS_WITH]-(other) +RETURN other.name ---- .Query Plan @@ -4879,59 +4524,64 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `other.name` | 15 | 24 | 0 | 0 | 0/0 | 0.413 | | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | other.name AS `other.name` | 15 | 24 | 48 | | 0/0 | 0.302 | | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Filter | 2 | NOT anon_2 = anon_0 | 15 | 24 | 0 | | 0/0 | 0.268 | | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +TriadicFilter | 10 | WHERE NOT (me)--(other) | 15 | 48 | 0 | 7216 | 0/0 | 0.298 | In Pipeline 3 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Apply | 9 | | 16 | 48 | 0 | | 0/0 | | | +| |\ +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Expand(All) | 4 | (anon_1)-[anon_2:FRIENDS_WITH]-(other) | 16 | 48 | 72 | | | | | +| | | +----+----------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 5 | anon_1, anon_0 | 24 | 24 | 0 | 4464 | 0/0 | 0.563 | Fused in Pipeline 2 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +TriadicBuild | 8 | (me)--(anon_1) | 24 | 24 | 0 | 1080 | 0/0 | 0.403 | In Pipeline 1 | +| | +----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Expand(All) | 6 | (me)-[anon_0:FRIENDS_WITH]-(anon_1) | 24 | 24 | 38 | | | | | +| | +----+----------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 7 | me:Person | 14 | 14 | 15 | 376 | 3/0 | 0.530 | Fused in Pipeline 0 | ++------------------+----+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 12, total allocated memory: 4928 +Total database accesses: 256, total allocated memory: 7376 ---- ====== -[[query-plan-eager]] -== Eager -// Eager +[[union-operators]] +== Union operators -The `Eager` operator causes all preceding operators to execute fully, for the whole dataset, before continuing execution. -This is done to ensure isolation between parts of the query plan that might otherwise affect each other. +Union operators in Cypher combine the results from multiple query parts by merging their rows. +For more information, see the page about the xref:clauses/union.adoc[`UNION`] clause. -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::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. -Alternatively, the records to be updated can be returned, followed by an update statement. +[[query-plan-union]] +=== Union +The `Union` operator concatenates the results from the right child operator with the results from the left child operator. -.Eager -====== +.Union +====== .Query [source, cypher] ---- PROFILE -MATCH (a:Person {name: 'me'}), (b:Person {name: 'Bob'}) -DETACH DELETE a, b -MERGE (:Person {name: 'me'}) +MATCH (p:Location) + RETURN p.name +UNION ALL +MATCH (p:Country) + RETURN p.name ---- .Query Plan @@ -4941,46 +4591,43 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | | 0 | 0 | 0 | 0 | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | 1 | | 0 | 0 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 2 | | 0 | 1 | 0 | | | | | -| |\ +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Merge | 3 | CREATE (anon_0:Person {name: $autostring_2}) | 0 | 1 | 3 | | | | | -| | | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +NodeIndexSeek | 4 | RANGE INDEX anon_0:Person(name) WHERE name = $autostring_2 | 0 | 0 | 1 | 3304 | 1/0 | 0.663 | Fused in Pipeline 3 | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Eager | 5 | read/delete conflict for variable: anon_0 (Operator: 6 vs 4, and 1 more conflicting operators) | 0 | 1 | 0 | 360 | 0/0 | 0.008 | In Pipeline 2 | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +DetachDelete | 6 | b | 0 | 1 | 4 | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DetachDelete | 7 | a | 0 | 1 | 5 | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Eager | 8 | read/delete conflict for variable: b (Operator: 6 vs 10, and 1 more conflicting operators), | 0 | 1 | 0 | 360 | 1/0 | 0.226 | Fused in Pipeline 1 | -| | | | read/set conflict for label: Person (Operator: 3 vs 10), | | | | | | | | -| | | | read/set conflict for property: name (Operator: 3 vs 10) | | | | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +MultiNodeIndexSeek | 9 | RANGE INDEX a:Person(name) WHERE name = $autostring_0, | 0 | 1 | 4 | 376 | 2/0 | 0.218 | In Pipeline 0 | -| | | RANGE INDEX b:Person(name) WHERE name = $autostring_1 | | | | | | | | -+---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| 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: 17, total allocated memory: 4184 +Total database accesses: 0, total allocated memory: 320 ---- ====== +[[aggregation-operators]] +== Aggregation operators + +Aggregation operators are used to compute summary statistics over groups of graph data, enabling operations such as counting nodes, relationships or properties. + [[query-plan-eager-aggregation]] -== Eager Aggregation -// EagerAggregation +=== Eager Aggregation The `EagerAggregation` operator evaluates a grouping expression and uses the result to group rows into different groupings. For each of these groupings, `EagerAggregation` will then evaluate all aggregation functions and return the result. @@ -5006,7 +4653,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -5033,8 +4680,7 @@ Total database accesses: 117, total allocated memory: 2664 [[query-plan-ordered-aggregation]] -== Ordered Aggregation -// OrderedAggregation +=== Ordered Aggregation 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. @@ -5059,7 +4705,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -5080,8 +4726,7 @@ Total database accesses: 3, total allocated memory: 352 [[query-plan-node-count-from-count-store]] -== Node Count From Count Store -// NodeCountFromCountStore +=== Node Count From Count Store The `NodeCountFromCountStore` operator uses the count store to answer questions about node counts. 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. @@ -5107,7 +4752,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -5126,164 +4771,23 @@ Total database accesses: 1, total allocated memory: 184 [[query-plan-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 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. - - -.RelationshipCountFromCountStore -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (p:Person)-[r:WORKS_IN]->() -RETURN count(r) AS jobs ----- - -.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 | jobs | 1 | 1 | 0 | | | | | -| | +--------------------------------------------+----------------+------+---------+----------------+ | | | -| +RelationshipCountFromCountStore | count( (:Person)-[:WORKS_IN]->() ) AS jobs | 1 | 1 | 1 | 120 | 0/0 | 0.625 | Fused in Pipeline 0 | -+----------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 1, total allocated memory: 184 ----- - -====== - - -[[query-plan-distinct]] -== Distinct -// Distinct - -The `Distinct` operator removes duplicate rows from the incoming stream of rows. -To ensure only distinct elements are returned, `Distinct` will pull in data lazily from its source and build up state. -This may lead to increased memory pressure in the system. - - -.Distinct -====== -.Query -[source, cypher] ----- -PROFILE -MATCH (l:Location)<-[:WORKS_IN]-(p:Person) -RETURN DISTINCT p ----- - -.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 | 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: 95, total allocated memory: 432 ----- - -====== - -[[query-plan-ordered-distinct]] -== Ordered Distinct -// OrderedDistinct - -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. - - -.OrderedDistinct -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (p:Person) -WHERE p.name STARTS WITH 'P' -RETURN DISTINCT p.name ----- - -.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) | Ordered by | Pipeline | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ -| +ProduceResults | `p.name` | 0 | 2 | 0 | | 0/0 | 0.046 | | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +OrderedDistinct | cache[p.name] AS `p.name` | 0 | 2 | 0 | 32 | 0/0 | 0.090 | `p.name` ASC | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+ | -| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.493 | p.name ASC | In Pipeline 0 | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ - -Total database accesses: 3, total allocated memory: 184 ----- - -====== +=== Relationship Count From Count Store - -[[query-plan-filter]] -== Filter -// Filter - -The `Filter` operator filters each row coming from the child operator, only passing through rows that evaluate the predicates to `true`. +The `RelationshipCountFromCountStore` operator uses the count store to answer questions about relationship counts. +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. -.Filter +.RelationshipCountFromCountStore ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -WHERE p.name =~ '^a.*' -RETURN p +MATCH (p:Person)-[r:WORKS_IN]->() +RETURN count(r) AS jobs ---- .Query Plan @@ -5293,43 +4797,42 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 14 | 0 | 0 | | | | | -| | +------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | cache[p.name] =~ $autostring_0 | 14 | 0 | 0 | | | | | -| | +------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexScan | RANGE INDEX p:Person(name) WHERE name IS NOT NULL, cache[p.name] | 14 | 14 | 15 | 120 | 0/1 | 0.763 | Fused in Pipeline 0 | -+-----------------+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | jobs | 1 | 1 | 0 | | | | | +| | +--------------------------------------------+----------------+------+---------+----------------+ | | | +| +RelationshipCountFromCountStore | count( (:Person)-[:WORKS_IN]->() ) AS jobs | 1 | 1 | 1 | 120 | 0/0 | 0.625 | Fused in Pipeline 0 | ++----------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 15, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== +[[filter-order-projection-operators]] +== Filter, order, and projection operators -[[query-plan-limit]] -== Limit -// Limit +The operators in this group handle how rows of data are transformed, filtered, and finalized for query results. +They are responsible for crafting the structure of the returned data, -The `Limit` operator returns the first `+n+` rows from the incoming input. +[[query-plan-empty-result]] +=== Empty Result +The `EmptyResult` operator eagerly loads all incoming data and discards it. -.Limit +.EmptyResult ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -RETURN p -LIMIT 3 +CREATE (:Person) ---- .Query Plan @@ -5339,44 +4842,41 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 3 | 3 | 0 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +Limit | 3 | 3 | 3 | 0 | 32 | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan| p:Person | 3 | 4 | 5 | 120 | 3/0 | 0,540 | Fused in Pipeline 0 | -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | | 1 | 0 | 0 | | | | +| | +-----------------+----------------+------+---------+ | | | +| +EmptyResult | | 1 | 0 | 0 | | | | +| | +-----------------+----------------+------+---------+ | | | +| +Create | (anon_0:Person) | 1 | 1 | 1 | 0/0 | 0.000 | Fused in Pipeline 0 | ++-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ -Total database accesses: 8, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[[query-plan-skip]] -== Skip -// Skip - -The `Skip` operator skips `+n+` rows from the incoming rows. +[[query-plan-produce-results]] +=== Produce Results +The `ProduceResults` operator prepares the result so that it is consumable by the user, such as transforming internal values to user values. +It is present in every single query that returns data to the user, and has little bearing on performance optimisation. -.Skip +.ProduceResults ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -RETURN p -ORDER BY p.id -SKIP 1 +MATCH (n) +RETURN n ---- .Query Plan @@ -5386,39 +4886,30 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | | | | | | -| | +----------------+----------------+------+---------+----------------+ | +------------+ | -| +NodeByLabelScan | p:Person | 18 | 18 | 19 | 120 | 3/0 | 0,157 | | Fused in Pipeline 0 | -+------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | n | 35 | 35 | 0 | | | | | +| | +---------+----------------+------+---------+----------------+ | | | +| +AllNodesScan | n | 35 | 35 | 36 | 120 | 3/0 | 0.508 | Fused in Pipeline 0 | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 71, total allocated memory: 512 +Total database accesses: 36, total allocated memory: 184 ---- ====== -[[query-plan-sort]] -== Sort -// Sort - -The `Sort` operator sorts rows by a provided key. -In order to sort the data, all data from the source operator needs to be pulled in eagerly and kept in the query state, which will lead to increased memory pressure in the system. +[[query-plan-filter]] +=== Filter +The `Filter` operator filters each row coming from the child operator, only passing through rows that evaluate the predicates to `true`. -.Sort +.Filter ====== .Query @@ -5426,8 +4917,8 @@ In order to sort the data, all data from the source operator needs to be pulled ---- PROFILE MATCH (p:Person) +WHERE p.name =~ '^a.*' RETURN p -ORDER BY p.name ---- .Query Plan @@ -5437,48 +4928,39 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | | | | | | -| | +--------------------+----------------+------+---------+----------------+ | +------------+ | -| +NodeByLabelScan |p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,221 | | Fused in Pipeline 0 | -+------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++-----------------+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p | 14 | 0 | 0 | | | | | +| | +------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | cache[p.name] =~ $autostring_0 | 14 | 0 | 0 | | | | | +| | +------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | RANGE INDEX p:Person(name) WHERE name IS NOT NULL, cache[p.name] | 14 | 14 | 15 | 120 | 0/1 | 0.763 | Fused in Pipeline 0 | ++-----------------+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 85, total allocated memory: 1272 +Total database accesses: 15, total allocated memory: 184 ---- ====== +[[query-plan-empty-row]] +=== Empty Row -[[query-plan-partial-sort]] -== Partial Sort -// PartialSort - -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. - +The `EmptyRow` operator returns a single row with no columns. -.PartialSort +.EmptyRow ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -WHERE p.name STARTS WITH 'P' -RETURN p -ORDER BY p.name, p.age +CYPHER runtime=slotted +FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- .Query Plan @@ -5486,49 +4968,52 @@ ORDER BY p.name, p.age ---- Planner COST -Runtime PIPELINED - -Runtime version {neo4j-version-minor} +Runtime SLOTTED -Batch size 128 +Runtime version {neo4j-version} -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.087 | | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +PartialSort | `p.name` ASC, `p.age` ASC | 0 | 2 | 0 | 544 | 0/0 | 0.184 | p.name ASC, p.age ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | -| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.362 | p.name ASC | Fused in Pipeline 0 | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ ++-----------------+--------------------------------------+----------------+------+---------+------------------------+ +| 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.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: 3, total allocated memory: 608 +Total database accesses: 304, total allocated memory: 64 ---- ====== -[[query-plan-top]] -== Top -// Top +[[query-plan-cache-properties]] +=== Cache Properties -The `Top` operator returns the first `+n+` rows sorted by a provided key. -Instead of sorting the entire input, only the top `+n+` rows are retained. +The `CacheProperties` operator reads nodes and relationship properties and caches them in the current row. +Future accesses to these properties can avoid reading from the store which will speed up the query. +In the plan below we will cache `l.name` before `Expand(All)` where there are fewer rows. -.Top +.CacheProperties ====== - .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -RETURN p -ORDER BY p.name -LIMIT 2 +MATCH (l:Location)<-[:WORKS_IN]-(p:Person) +RETURN + l.name AS location, + p.name AS name ---- .Query Plan @@ -5538,49 +5023,46 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | | | | | | -| | +----------------------+----------------+------+---------+----------------+ | +------------+ | -| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,166 | | 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: 85, total allocated memory: 1264 +Total database accesses: 107, total allocated memory: 200 ---- ====== -[[query-plan-partial-top]] -== Partial Top -// PartialTop +[[query-plan-projection]] +=== Projection -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. +For each incoming row, the `Projection` operator evaluates a set of expressions and produces a row with the results of the expressions. -.PartialTop +.Projection ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -WHERE p.name STARTS WITH 'P' -RETURN p -ORDER BY p.name, p.age -LIMIT 2 +RETURN 'hello' AS greeting ---- .Query Plan @@ -5590,46 +5072,40 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.093 | | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +PartialTop | `p.name` ASC, `p.age` ASC LIMIT 2 | 0 | 2 | 0 | 640 | 0/0 | 0.870 | p.name ASC, p.age ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | -| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.556 | p.name ASC | Fused in Pipeline 0 | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ ++-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | greeting | 1 | 1 | 0 | | | | +| | +---------------------------+----------------+------+---------+ | | | +| +Projection | $autostring_0 AS greeting | 1 | 1 | 0 | 0/0 | 0.000 | Fused in Pipeline 0 | ++-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -Total database accesses: 3, total allocated memory: 704 +Total database accesses: 0, total allocated memory: 184 ---- ====== -[[query-plan-union]] -== Union -// Union - -The `Union` operator concatenates the results from the right child operator with the results from the left child operator. +[[query-plan-project-endpoints]] +=== Project Endpoints +The `ProjectEndpoints` operator projects the start and end node of a relationship. -.Union +.ProjectEndpoints ====== + .Query [source, cypher] ---- PROFILE -MATCH (p:Location) - RETURN p.name -UNION ALL -MATCH (p:Country) - RETURN p.name +CREATE (n)-[p:KNOWS]->(m) +WITH p AS r +MATCH (u)-[r]->(v) +RETURN u, v ---- .Query Plan @@ -5639,52 +5115,50 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| 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 | -+--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | u, v | 1 | 1 | 2 | 0 | | | | +| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 1 | | 1 | 1 | 0 | | | | | +| |\ +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| | +ProjectEndpoints | 2 | (u)-[r]->(v) | 1 | 1 | 0 | | | | | +| | | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 3 | r | 1 | 1 | 0 | 4328 | 0/0 | 0.194 | Fused in Pipeline 2 | +| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Eager | 4 | read/create conflict (Operator: 6 vs 2) | 1 | 1 | 0 | 368 | 0/0 | 0.025 | In Pipeline 1 | +| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Projection | 5 | p AS r | 1 | 1 | 0 | | | | | +| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| +Create | 6 | (n), (m), (n)-[p:KNOWS]->(m) | 1 | 1 | 3 | | 0/0 | 0.000 | Fused in Pipeline 0 | ++---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 320 +Total database accesses: 5, total allocated memory: 4920 ---- ====== -[[query-plan-unwind]] -== Unwind -// Unwind +[[query-plan-distinct]] +=== Distinct -The `Unwind` operator returns one row per item in a list. +The `Distinct` operator removes duplicate rows from the incoming stream of rows. +To ensure only distinct elements are returned, `Distinct` will pull in data lazily from its source and build up state. +This may lead to increased memory pressure in the system. -.Unwind +.Distinct ====== - .Query [source, cypher] ---- PROFILE -UNWIND range(1, 5) AS value -RETURN value +MATCH (l:Location)<-[:WORKS_IN]-(p:Person) +RETURN DISTINCT p ---- .Query Plan @@ -5694,43 +5168,47 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | value | 10 | 5 | 0 | | | | -| | +----------------------------------------+----------------+------+---------+ | | | -| +Unwind | range($autoint_0, $autoint_1) AS value | 10 | 5 | 0 | 0/0 | 0.000 | 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 | 29 | 0 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Distinct | 1 | p | 14 | 14 | 0 | 352 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 15 | 15 | 30 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 25 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 4 | l:Location | 10 | 10 | 11 | 248 | 8/0 | 0.758 | Fused in Pipeline 0 | ++------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 184 +Total database accesses: 95, total allocated memory: 432 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-unwind]] -== Partitioned Unwind -// PartitionedUnwind -// New in 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. +[[query-plan-ordered-distinct]] +=== Ordered Distinct +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. -.PartitionedUnwind + +.OrderedDistinct ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -UNWIND range(1, 5) AS value -RETURN value +MATCH (p:Person) +WHERE p.name STARTS WITH 'P' +RETURN DISTINCT p.name ---- .Query Plan @@ -5738,45 +5216,42 @@ RETURN value ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} 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 | -+--------------------+----+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ +| +ProduceResults | `p.name` | 0 | 2 | 0 | | 0/0 | 0.046 | | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedDistinct | cache[p.name] AS `p.name` | 0 | 2 | 0 | 32 | 0/0 | 0.090 | `p.name` ASC | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+ | +| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.493 | p.name ASC | In Pipeline 0 | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ -Total database accesses: 0 +Total database accesses: 3, total allocated memory: 184 ---- ====== -[[query-plan-exhaustive-limit]] -== Exhaustive Limit -// LockNodes - changed in 4.3 -// ExhaustiveLimit - -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 +[[query-plan-procedure-call]] +=== Procedure Call +The `ProcedureCall` operator indicates an invocation to a procedure. -.ExhaustiveLimit +.ProcedureCall ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -SET p.seen = true -RETURN p -LIMIT 3 +CALL db.labels() YIELD label +RETURN * +ORDER BY label ---- .Query Plan @@ -5786,47 +5261,39 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | p | 3 | 3 | 10 | 0 | | | | -| | +----+---------------+----------------+------+---------+----------------+ | | | -| +ExhaustiveLimit | 1 | 3 | 3 | 3 | 0 | 32 | | | | -| | +----+---------------+----------------+------+---------+----------------+ | | | -| +SetProperty | 2 | p.seen = true | 17 | 17 | 34 | | | | | -| | +----+---------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | 3 | p:Person | 17 | 17 | 18 | 240 | 3/0 | 1.966 | Fused in Pipeline 0 | -+------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | label | 10 | 4 | 0 | | 0/0 | 0.091 | | | +| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Sort | label ASC | 10 | 4 | 0 | 536 | 0/0 | 0.178 | label ASC | In Pipeline 1 | +| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProcedureCall | db.labels() :: (label :: STRING) | 10 | 4 | | | | | | Fused in Pipeline 0 | ++-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 62, total allocated memory: 304 +Total database accesses: ?, total allocated memory: 600 ---- ====== +[[query-plan-unwind]] +=== Unwind -[[query-plan-optional]] -== Optional -// Optional - -The `Optional` operator is used to solve some xref::clauses/optional-match.adoc[OPTIONAL MATCH] queries. -It will pull data from its source, simply passing it through if any data exists. -However, if no data is returned by its source, `Optional` will yield a single row with all columns set to `null`. - +The `Unwind` operator returns one row per item in a list. -.Optional +.Unwind ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person {name: 'me'}) -OPTIONAL MATCH (q:Person {name: 'Lulu'}) -RETURN p, q +UNWIND range(1, 5) AS value +RETURN value ---- .Query Plan @@ -5836,48 +5303,40 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | p, q | 1 | 1 | 0 | | 2/0 | 0.079 | In Pipeline 2 | -| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +Apply | | 1 | 1 | 0 | | 0/0 | 0.096 | | -| |\ +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| | +Optional | p | 1 | 1 | 0 | 768 | 0/0 | 0.043 | In Pipeline 2 | -| | | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 2152 | 1/0 | 0.098 | In Pipeline 1 | -| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.364 | In Pipeline 0 | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++-----------------+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | value | 10 | 5 | 0 | | | | +| | +----------------------------------------+----------------+------+---------+ | | | +| +Unwind | range($autoint_0, $autoint_1) AS value | 10 | 5 | 0 | 0/0 | 0.000 | Fused in Pipeline 0 | ++-----------------+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -Total database accesses: 3, total allocated memory: 3000 +Total database accesses: 0, total allocated memory: 184 ---- ====== +[role=label--new-5.17] +[[query-plan-partitioned-unwind]] +=== Partitioned Unwind -[[query-plan-project-endpoints]] -== Project Endpoints -// ProjectEndpoints - -The `ProjectEndpoints` operator projects the start and end node of a relationship. - +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. -.ProjectEndpoints +.PartitionedUnwind ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -CREATE (n)-[p:KNOWS]->(m) -WITH p AS r -MATCH (u)-[r]->(v) -RETURN u, v +UNWIND range(1, 5) AS value +RETURN value ---- .Query Plan @@ -5885,51 +5344,48 @@ RETURN u, v ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | u, v | 1 | 1 | 2 | 0 | | | | -| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 1 | | 1 | 1 | 0 | | | | | -| |\ +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| | +ProjectEndpoints | 2 | (u)-[r]->(v) | 1 | 1 | 0 | | | | | -| | | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 3 | r | 1 | 1 | 0 | 4328 | 0/0 | 0.194 | Fused in Pipeline 2 | -| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Eager | 4 | read/create conflict (Operator: 6 vs 2) | 1 | 1 | 0 | 368 | 0/0 | 0.025 | In Pipeline 1 | -| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Projection | 5 | p AS r | 1 | 1 | 0 | | | | | -| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| +Create | 6 | (n), (m), (n)-[p:KNOWS]->(m) | 1 | 1 | 3 | | 0/0 | 0.000 | Fused in Pipeline 0 | -+---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------+----+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| 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: 5, total allocated memory: 4920 +Total database accesses: 0 ---- ====== -[[query-plan-projection]] -== Projection -// Projection +[[sort-limit-operators]] +== Sort and limit operators -For each incoming row, the `Projection` operator evaluates a set of expressions and produces a row with the results of the expressions. +The operators in this group manage result set ordering and control the flow of data by limiting or skipping rows. +They optimize query performance and ensure users receive results in the desired order and quantity. +[[query-plan-sort]] +=== Sort -.Projection +The `Sort` operator sorts rows by a provided key. +In order to sort the data, all data from the source operator needs to be pulled in eagerly and kept in the query state, which will lead to increased memory pressure in the system. + +.Sort ====== .Query [source, cypher] ---- PROFILE -RETURN 'hello' AS greeting +MATCH (p:Person) +RETURN p +ORDER BY p.name ---- .Query Plan @@ -5939,43 +5395,46 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | greeting | 1 | 1 | 0 | | | | -| | +---------------------------+----------------+------+---------+ | | | -| +Projection | $autostring_0 AS greeting | 1 | 1 | 0 | 0/0 | 0.000 | Fused in Pipeline 0 | -+-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| 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 | | | | | | +| | +--------------------+----------------+------+---------+----------------+ | +------------+ | +| +NodeByLabelScan |p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,221 | | Fused in Pipeline 0 | ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 0, total allocated memory: 184 +Total database accesses: 85, total allocated memory: 1272 ---- ====== -[[query-plan-shortest-path]] -== Shortest path -// ShortestPath +[[query-plan-partial-sort]] +=== Partial Sort -The `ShortestPath` operator finds one or all shortest paths between two previously matched node variables. -This operator is used for the xref:patterns/reference.adoc#shortest-functions[`shortestPath()` and `allShortestPaths`] functions. +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. -.ShortestPath +.PartialSort ====== .Query [source, cypher] ---- PROFILE -MATCH - (andy:Person {name: 'Andy'}), - (mattias:Person {name: 'Mattias'}), - p = shortestPath((andy)-[*]-(mattias)) +MATCH (p:Person) +WHERE p.name STARTS WITH 'P' RETURN p +ORDER BY p.name, p.age ---- .Query Plan @@ -5985,54 +5444,45 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | p | 1 | 1 | 0 | | 1/0 | 0.241 | | -| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +ShortestPath | p = (andy)-[anon_0*]-(mattias) | 1 | 1 | 1 | 1424 | | | In Pipeline 1 | -| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | RANGE INDEX andy:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 120 | 1/1 | 0.308 | In Pipeline 0 | -| | RANGE INDEX mattias:Person(name) WHERE name = $autostring_1 | | | | | | | | -+---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.087 | | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +PartialSort | `p.name` ASC, `p.age` ASC | 0 | 2 | 0 | 544 | 0/0 | 0.184 | p.name ASC, p.age ASC | In Pipeline 1 | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | +| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.362 | p.name ASC | Fused in Pipeline 0 | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -Total database accesses: 5, total allocated memory: 1488 +Total database accesses: 3, total allocated memory: 608 ---- ====== -[role=label--new-5.21] -[[query-plan-stateful-shortest-path-into]] -== StatefulShortestPath(Into) -// StatefulShortestPath(Into) -//// -[source, cypher, role=test-setup] ----- -DROP INDEX range_person_name IF EXISTS; -CREATE CONSTRAINT person_name_unique IF NOT EXISTS FOR (p:Person) REQUIRE (p.name) IS UNIQUE; ----- -//// +[[query-plan-top]] +=== Top -The `StatefulShortestPath(Into)` operator finds shortest paths between a start node and a single target node. -It uses a bidirectional breadth-first search (BFS) algorithm, which performs two BFS invocations at the same time, one from the left boundary node and one from the right boundary node. -Once a node is found by both BFS invocations, which indicates that it can be reached from both boundary nodes, the algorithm successfully terminates. -If one of the BFS invocations exhausts its search before intersecting, either because no further nodes can be reached or because the maximum number of hops has been reached, then there is no valid path between the boundary nodes and the algorithm terminates. +The `Top` operator returns the first `+n+` rows sorted by a provided key. +Instead of sorting the entire input, only the top `+n+` rows are retained. -.StatefulShortestPath(Into) +.Top ====== .Query [source, cypher] ---- PROFILE -MATCH - p = ALL SHORTEST (chris:Person {name: 'Chris'})(()-[]-()-[]-()){1,}(stefan:Person {name: 'Stefan'}) +MATCH (p:Person) RETURN p +ORDER BY p.name +LIMIT 2 ---- .Query Plan @@ -6042,48 +5492,48 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | p | 2 | 2 | 0 | 0 | 0/0 | 0.039 | | -| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | (chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (stefan) AS p | 2 | 2 | 0 | | 0/0 | 1.365 | | -| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (stefan) | 2 | 2 | 39 | 22237 | 1/0 | 37.376 | In Pipeline 1 | -| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 376 | 1/1 | 10.245 | In Pipeline 0 | -| | | UNIQUE stefan:Person(name) WHERE name = $autostring_1 | | | | | | | | -+-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ - -Total database accesses: 43, total allocated memory: 22557 ++------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| 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 ---- ====== -[role=label--new-5.21] -[[query-plan-stateful-shortest-path-all]] -== StatefulShortestPath(All) -// StatefulShortestPath(All) -The `StatefulShortestPath(All)` operator finds shortest paths from a single node to multiple target nodes. -It uses a breadth-first search algorithm. +[[query-plan-partial-top]] +=== Partial Top + +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. -.StatefulShortestPath(All) +.PartialTop ====== .Query [source, cypher] ---- PROFILE -MATCH - p = ALL SHORTEST (chris:Person {name:'Chris'})(()-[]-()-[]-()){1,}(location:Location) -RETURN length(p) AS pathLength, location.name AS locationName +MATCH (p:Person) +WHERE p.name STARTS WITH 'P' +RETURN p +ORDER BY p.name, p.age +LIMIT 2 ---- .Query Plan @@ -6093,55 +5543,42 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | pathLength, locationName | 14 | 20 | 0 | 0 | 0/0 | 0.074 | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length((chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (location)) AS pathLength, | 14 | 20 | 40 | | 1/0 | 6.828 | | -| | | | location.name AS locationName | | | | | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(All) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (location) | 14 | 20 | 179 | 37663 | 1/0 | 52.849 | In Pipeline 1 | -| | | | expanding from: chris | | | | | | | | -| | | | inlined predicates: location:Location | | | | | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +NodeUniqueIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 0/1 | 9.078 | In Pipeline 0 | -+----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ - -Total database accesses: 221, total allocated memory: 37983 ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.093 | | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +PartialTop | `p.name` ASC, `p.age` ASC LIMIT 2 | 0 | 2 | 0 | 640 | 0/0 | 0.870 | p.name ASC, p.age ASC | In Pipeline 1 | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | +| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.556 | p.name ASC | Fused in Pipeline 0 | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +Total database accesses: 3, total allocated memory: 704 ---- -//// -[source, cypher, role=test-setup] ----- -DROP CONSTRAINT person_name_unique IF EXISTS; -CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name); ----- -//// ====== +[[query-plan-limit]] +=== Limit -[[query-plan-empty-row]] -== Empty Row -// EmptyRow - -The `EmptyRow` operator returns a single row with no columns. - +The `Limit` operator returns the first `+n+` rows from the incoming input. -.EmptyRow +.Limit ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) +MATCH (p:Person) +RETURN p +LIMIT 3 ---- .Query Plan @@ -6149,51 +5586,46 @@ FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- Planner COST -Runtime SLOTTED +Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} -+-----------------+--------------------------------------+----------------+------+---------+------------------------+ -| 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.age = value | 1 | 0 | 184 | 2/0 | -| | | +--------------------------------------+----------------+------+---------+------------------------+ -| | +NodeByLabelScan | anon_0:Person | 35 | 108 | 111 | 3/0 | -| | +--------------------------------------+----------------+------+---------+------------------------+ -| +EmptyRow | | 1 | 1 | 0 | 0/0 | -+--------------------+--------------------------------------+----------------+------+---------+------------------------+ +Batch size 128 -Total database accesses: 304, total allocated memory: 64 ++-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p | 3 | 3 | 0 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +Limit | 3 | 3 | 3 | 0 | 32 | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan| p:Person | 3 | 4 | 5 | 120 | 3/0 | 0,540 | Fused in Pipeline 0 | ++-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 8, total allocated memory: 184 ---- ====== +[[query-plan-exhaustive-limit]] +=== Exhaustive Limit +// LockNodes - changed in 4.3 -[[query-plan-procedure-call]] -== Procedure Call -// ProcedureCall - -The `ProcedureCall` operator indicates an invocation to a procedure. +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 -.ProcedureCall +.ExhaustiveLimit ====== .Query [source, cypher] ---- PROFILE -CALL db.labels() YIELD label -RETURN * -ORDER BY label +MATCH (p:Person) +SET p.seen = true +RETURN p +LIMIT 3 ---- .Query Plan @@ -6203,45 +5635,43 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | label | 10 | 4 | 0 | | 0/0 | 0.091 | | | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Sort | label ASC | 10 | 4 | 0 | 536 | 0/0 | 0.178 | label ASC | In Pipeline 1 | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProcedureCall | db.labels() :: (label :: STRING) | 10 | 4 | | | | | | 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 | 3 | 10 | 0 | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +ExhaustiveLimit | 1 | 3 | 3 | 3 | 0 | 32 | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +SetProperty | 2 | p.seen = true | 17 | 17 | 34 | | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 3 | p:Person | 17 | 17 | 18 | 240 | 3/0 | 1.966 | Fused in Pipeline 0 | ++------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: ?, total allocated memory: 600 +Total database accesses: 62, total allocated memory: 304 ---- ====== +[[query-plan-skip]] +=== Skip -[[query-plan-cache-properties]] -== Cache Properties -// CacheProperties - -The `CacheProperties` operator reads nodes and relationship properties and caches them in the current row. -Future accesses to these properties can avoid reading from the store which will speed up the query. -In the plan below we will cache `l.name` before `Expand(All)` where there are fewer rows. - +The `Skip` operator skips `+n+` rows from the incoming rows. -.CacheProperties +.Skip ====== + .Query [source, cypher] ---- PROFILE -MATCH (l:Location)<-[:WORKS_IN]-(p:Person) -RETURN - l.name AS location, - p.name AS name +MATCH (p:Person) +RETURN p +ORDER BY p.id +SKIP 1 ---- .Query Plan @@ -6251,35 +5681,37 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 -+------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| 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 | -+------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| 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: 107, total allocated memory: 200 +Total database accesses: 71, total allocated memory: 512 ---- ====== +[[data-modification-operators]] +== Data modification operators + +The operators in this group modify graph data by creating, deleting, and altering nodes, relationships, and properties. + [[query-plan-create]] -== Create (nodes and relationships) -// Create +=== Create The `Create` operator is used to create nodes and relationships. @@ -6304,7 +5736,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6326,12 +5758,10 @@ Total database accesses: 7, total allocated memory: 184 [[query-plan-delete]] -== Delete (nodes and relationships) -// Delete +=== Delete The `Delete` operator is used to delete a node or a relationship. - .Delete ====== @@ -6350,7 +5780,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6373,8 +5803,7 @@ Total database accesses: 13, total allocated memory: 216 [[query-plan-detach-delete]] -== Detach Delete -// DetachDelete +=== Detach Delete The `DetachDelete` operator is used in all queries containing the xref::clauses/delete.adoc[DETACH DELETE] clause, when deleting nodes and their relationships. @@ -6397,7 +5826,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6413,24 +5842,274 @@ Batch size 128 | +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 21/0 | 12,439 | Fused in Pipeline 0 | +------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 112, total allocated memory: 200 +Total database accesses: 112, total allocated memory: 200 +---- + +====== + +[[query-plan-merge]] +=== Merge +// ConditionalApply -- changed in 4.3 to Merge. +// AntiConditionalApply -- removed in 4.3 (by Merge). + +The `Merge` operator will either read or create nodes and/or relationships. + +If matches are found it will execute the provided `ON MATCH` operations foreach incoming row. +If no matches are found instead nodes and relationships are created and all `ON CREATE` operations are run. + + +.Merge +====== + +.Query +[source, cypher] +---- +PROFILE +MERGE (p:Person {name: 'Andy'}) +ON MATCH SET p.existed = true +ON CREATE SET p.existed = false +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version} + +Batch size 128 + ++-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | | 1 | 0 | 0 | | | | | +| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | | 1 | 0 | 0 | | | | | +| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Merge | CREATE (p:Person {name: $autostring_0}), ON MATCH SET p.existed = true, | 1 | 1 | 2 | | | | | +| | | ON CREATE SET p.existed = false | | | | | | | | +| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.749 | Fused in Pipeline 0 | ++-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 4, total allocated memory: 184 +---- + +====== + + +[[query-plan-locking-merge]] +=== Locking Merge + +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 +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (s:Person {name: 'me'}) +MERGE (s)-[:FRIENDS_WITH]->(s) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version} + +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 | | | | | +| |\ +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +LockingMerge | 3 | CREATE (s)-[anon_0:FRIENDS_WITH]->(s), LOCK(s) | 1 | 1 | 1 | | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(Into) | 4 | (s)-[anon_0:FRIENDS_WITH]->(s) | 0 | 0 | 10 | 904 | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 5 | s | 1 | 3 | 0 | 2280 | 2/0 | 0.460 | Fused in Pipeline 1 | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | 6 | RANGE INDEX s:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 1/0 | 0.211 | In Pipeline 0 | ++-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 15, total allocated memory: 2232 +---- + +====== + + + +[[query-plan-foreach]] +=== Foreach +// Foreach + +The `Foreach` operator executes a nested loop between the left child operator and the right child operator. +In an analogous manner to the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator, it takes a row from the left-hand side and, using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[Argument] operator, provides it to the operator tree on the right-hand side. +`Foreach` will yield all the rows coming in from the left-hand side; all results from the right-hand side are pulled in and discarded. + + +.Foreach +====== + +.Query +[source, cypher] +---- +PROFILE +FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime SLOTTED + +Runtime version {neo4j-version} + ++-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ +| 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], CREATE (anon_0:Person {age: value}) | 1 | 1 | 9 | 0/0 | ++-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ + +Total database accesses: 9, total allocated memory: 64 +---- + +====== + +[[query-plan-subquery-foreach]] +=== SubqueryForeach + +`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 +====== + +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + +.Query +[source, cypher] +---- +PROFILE +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL (line) { + CREATE (a: Artist {name: line[0]}) +} +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version} + +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-transaction-foreach]] +=== TransactionForeach + +`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 +====== + +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + +.Query +[source, cypher] +---- +PROFILE +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL (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} +Batch size 128 -// MergeCreateNode -- removed in 4.3 +++---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | 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 | + +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -// MergeCreateRelationship -- removed in 4.3 + Total database accesses: 12, total allocated memory: 5704 +---- +====== [[query-plan-set-labels]] -== Set Labels -// SetLabels +=== Set Labels The `SetLabels` operator is used when setting labels on a node. - .SetLabels ====== @@ -6449,7 +6128,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6472,12 +6151,10 @@ Total database accesses: 58, total allocated memory: 184 [[query-plan-remove-labels]] -== Remove Labels -// RemoveLabels +=== Remove Labels The `RemoveLabels` operator is used when deleting labels from a node. - .RemoveLabels ====== @@ -6496,7 +6173,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6519,12 +6196,10 @@ Total database accesses: 51, total allocated memory: 184 [[query-plan-set-node-properties-from-map]] -== Set Node Properties From Map -// SetNodePropertiesFromMap +=== Set Node Properties From Map The `SetNodePropertiesFromMap` operator is used when setting properties from a map on a node. - .SetNodePropertiesFromMap ====== @@ -6543,7 +6218,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6566,12 +6241,10 @@ Total database accesses: 141, total allocated memory: 184 [[query-plan-set-relationship-properties-from-map]] -== Set Relationship Properties From Map -// SetRelationshipPropertiesFromMap +=== Set Relationship Properties From Map The `SetRelationshipPropertiesFromMap` operator is used when setting properties from a map on a relationship. - .SetRelationshipPropertiesFromMap ====== @@ -6590,7 +6263,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6613,12 +6286,11 @@ Total database accesses: 112, total allocated memory: 184 [[query-plan-set-property]] -== Set Property +=== Set Property // SetProperty The `SetProperty` operator is used when setting a property on a node or relationship. - .SetProperty ====== @@ -6637,7 +6309,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6659,12 +6331,10 @@ Total database accesses: 106, total allocated memory: 184 ====== [[query-plan-set-properties]] -== Set Properties -// SetProperties +=== Set Properties The `SetProperties` operator is used when setting multiple properties on a node or relationship. - .SetProperties ====== @@ -6683,7 +6353,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -6704,11 +6374,234 @@ Total database accesses: 141, total allocated memory: 312 ====== -[[query-plan-create-constraint]] -== Create Constraint -// CreateConstraint + +[[query-plan-load-csv]] +=== Load CSV + +The `LoadCSV` operator loads data from a CSV source into the query. +It is used whenever the xref::clauses/load-csv.adoc[LOAD CSV] clause is used in a query. + + +.LoadCSV +====== + +.Query +[source, cypher] +---- +PROFILE +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +RETURN line +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version} + +Batch size 128 + ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | line | 10 | 4 | 0 | | 0/0 | 0.210 | | +| | +---------+----------------+------+---------+----------------+------------------------+-----------+ | +| +LoadCSV | line | 10 | 4 | 0 | 72 | | | In Pipeline 1 | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ + +Total database accesses: 0, total allocated memory: 184 +---- + +====== + + +[[query-plan-eager]] +=== Eager + +The `Eager` operator causes all preceding operators to execute fully, for the whole dataset, before continuing execution. +This is done to ensure isolation between parts of the query plan that might otherwise affect each other. + +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::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. +Alternatively, the records to be updated can be returned, followed by an update statement. + + +.Eager +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (a:Person {name: 'me'}), (b:Person {name: 'Bob'}) +DETACH DELETE a, b +MERGE (:Person {name: 'me'}) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version} + +Batch size 128 + ++---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 0 | 0 | 0 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 0 | 0 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 2 | | 0 | 1 | 0 | | | | | +| |\ +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Merge | 3 | CREATE (anon_0:Person {name: $autostring_2}) | 0 | 1 | 3 | | | | | +| | | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeek | 4 | RANGE INDEX anon_0:Person(name) WHERE name = $autostring_2 | 0 | 0 | 1 | 3304 | 1/0 | 0.663 | Fused in Pipeline 3 | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Eager | 5 | read/delete conflict for variable: anon_0 (Operator: 6 vs 4, and 1 more conflicting operators) | 0 | 1 | 0 | 360 | 0/0 | 0.008 | In Pipeline 2 | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +DetachDelete | 6 | b | 0 | 1 | 4 | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DetachDelete | 7 | a | 0 | 1 | 5 | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Eager | 8 | read/delete conflict for variable: b (Operator: 6 vs 10, and 1 more conflicting operators), | 0 | 1 | 0 | 360 | 1/0 | 0.226 | Fused in Pipeline 1 | +| | | | read/set conflict for label: Person (Operator: 3 vs 10), | | | | | | | | +| | | | read/set conflict for property: name (Operator: 3 vs 10) | | | | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +MultiNodeIndexSeek | 9 | RANGE INDEX a:Person(name) WHERE name = $autostring_0, | 0 | 1 | 4 | 376 | 2/0 | 0.218 | In Pipeline 0 | +| | | RANGE INDEX b:Person(name) WHERE name = $autostring_1 | | | | | | | | ++---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 17, total allocated memory: 4184 +---- + +====== + + +[[query-plan-assert-same-node]] +=== Assert Same Node + +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 +====== + +.Query +[source, cypher] +---- +PROFILE +CYPHER runtime=slotted +MERGE (t:Team {name: 'Engineering', id: 42}) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime SLOTTED + +Runtime version {neo4j-version} + ++---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | | 1 | 0 | 0 | 0/0 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +EmptyResult | | 1 | 0 | 0 | 0/0 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | 0/0 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +AssertSameNode | t | 0 | 1 | 0 | 0/0 | +| |\ +-------------------------------------------------------+----------------+------+---------+------------------------+ +| | +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(id) WHERE id = $autoint_1 | 1 | 1 | 1 | 0/1 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 1 | 1 | 0/1 | ++---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ + +Total database accesses: 2, total allocated memory: 64 +---- + +====== +[role=label--new-5.8] +[[query-plan-assert-same-relationship]] +=== Assert Same Relationship + +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 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 +====== + +.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} + ++-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| 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 +---- + +====== + + +[[schema-system-operators]] +== Schema and system operators + +The operators in this group manage the schema and system-level operations within the database. +They handle tasks such as creating or dropping constraints and indexes, showing or terminating transactions, and listing the available procedures, functions, and settings. + +[[query-plan-create-constraint]] +=== Create Constraint + The `CreateConstraint` operator creates a constraint. This constraint can have any of the available constraint types: @@ -6720,7 +6613,6 @@ This constraint can have any of the available constraint types: The following query will create a property uniqueness constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label. - .CreateConstraint ====== @@ -6739,7 +6631,7 @@ Planner ADMINISTRATION Runtime SCHEMA -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-------------------+------------------------------------------------------------------+ | Operator | Details | @@ -6754,8 +6646,7 @@ Total database accesses: ? [[query-plan-do-nothing-if-exists-constraint]] -== Do Nothing If Exists (constraint) -// DoNothingIfExists(CONSTRAINT) +=== Do Nothing If Exists (constraint) 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. @@ -6781,7 +6672,7 @@ Planner ADMINISTRATION Runtime SCHEMA -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +--------------------------------+------------------------------------------------------------------+ | Operator | Details | @@ -6799,13 +6690,10 @@ Total database accesses: ? ====== [[query-plan-drop-constraint]] -== Drop Constraint -// DropConstraint +=== Drop Constraint The `DropConstraint` operator removes a constraint using the name of the constraint, no matter the type. - - .DropConstraint ====== @@ -6823,7 +6711,7 @@ Planner ADMINISTRATION Runtime SCHEMA -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-----------------+---------------------------------+ | Operator | Details | @@ -6838,13 +6726,11 @@ Total database accesses: ? [[query-plan-show-constraints]] -== Show Constraints -// ShowConstraints +=== Show Constraints The `ShowConstraints` operator lists constraints. It may include filtering on constraint type and can have either default or full output. - .ShowConstraints ====== @@ -6862,7 +6748,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +------------------+-------------------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -6879,8 +6765,7 @@ Total database accesses: 2, total allocated memory: 64 [[query-plan-create-index]] -== Create Index -// CreateIndex +=== Create Index The `CreateIndex` operator creates an index. @@ -6906,7 +6791,7 @@ Planner ADMINISTRATION Runtime SCHEMA -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +--------------+-----------------------------------------------+ | Operator | Details | @@ -6921,8 +6806,7 @@ Total database accesses: ? [[query-plan-do-nothing-if-exists-index]] -== Do Nothing If Exists (index) -// DoNothingIfExists(INDEX) +=== Do Nothing If Exists (index) To not get an error creating the same index twice, we use the `DoNothingIfExists` operator for indexes. This will make sure no other index with the given name or schema already exists before the `CreateIndex` operator creates an index. @@ -6948,7 +6832,7 @@ Planner ADMINISTRATION Runtime SCHEMA -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +---------------------------+----------------------------------------------------+ | Operator | Details | @@ -6965,12 +6849,10 @@ Total database accesses: ? [[query-plan-drop-index]] -== Drop Index -// DropIndex +=== Drop Index The `DropIndex` operator removes an index using the name of the index. - .DropIndex ====== @@ -6988,7 +6870,7 @@ Planner ADMINISTRATION Runtime SCHEMA -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +------------+---------------+ | Operator | Details | @@ -7003,13 +6885,11 @@ Total database accesses: ? [[query-plan-show-indexes]] -== Show Indexes -// ShowIndexes +=== Show Indexes The `ShowIndexes` operator lists indexes. It may include filtering on index type and can have either default or full output. - .ShowIndexes ====== @@ -7027,7 +6907,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-----------------+-------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -7045,8 +6925,7 @@ Total database accesses: 2, total allocated memory: 64 [[query-plan-show-functions]] -== Show Functions -// ShowFunctions +=== Show Functions The `ShowFunctions` operator lists functions. It may include filtering on built-in vs user-defined functions as well as if a given user can execute the function. @@ -7070,7 +6949,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-----------------+-----------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -7087,13 +6966,11 @@ Total database accesses: 0, total allocated memory: 64 [[query-plan-show-procedures]] -== Show Procedures -// ShowProcedures +=== Show Procedures The `ShowProcedures` operator lists procedures. It may include filtering on whether a given user can execute the procedure and can have either default or full output. - .ShowProcedures ====== @@ -7111,7 +6988,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-----------------+----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -7127,8 +7004,7 @@ Total database accesses: 0, total allocated memory: 64 ====== [[query-plan-show-settings]] -== Show Settings -// ShowSettings +=== Show Settings The `ShowSettings` operator lists configuration settings. @@ -7149,7 +7025,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-----------------+---------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -7165,13 +7041,11 @@ Total database accesses: 0, total allocated memory: 64 ====== [[query-plan-show-transactions]] -== Show Transactions -// ShowTransactions +=== Show Transactions The `ShowTransactions` operator lists transactions. It may include filtering on given ids and can have either default or full output. - .ShowTransactions ====== @@ -7189,7 +7063,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-------------------+-----------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -7207,12 +7081,10 @@ Total database accesses: 0, total allocated memory: 64 [[query-plan-terminate-transactions]] -== Terminate Transactions -// TerminateTransactions +=== Terminate Transactions The `TerminateTransactions` operator terminates transactions by ID. - .TerminateTransactions ====== @@ -7230,7 +7102,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +------------------------+--------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | diff --git a/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc b/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc index b8ba76a7e..ad8cfbb3a 100644 --- a/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc +++ b/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc @@ -16,7 +16,7 @@ The overall goal of manual query performance optimization is to ensure that only 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. +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. @@ -248,8 +248,8 @@ This setting is experimental, and using it in a production environment is discou 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. +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/current/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/current/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/current/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. @@ -308,7 +308,7 @@ During times of known high load, `replan=skip` can be useful to not introduce un === Cypher infer schema parts For some queries, the planner can infer predicates such as labels or types from the graph structure, thereby enhancing its ability to estimate the number of rows each operator will produce. -(See xref:planning-and-tuning/execution-plans.adoc#runtimes-reading-execution-plans[Understanding execution plans - Reading execution plans] for more information about the role of operators and estimated row counts in query execution plans.) +(See xref:planning-and-tuning/execution-plans.adoc#reading-execution-plans[Understanding execution plans - Reading execution plans] for more information about the role of operators and estimated row counts in query execution plans.) The option `inferSchemaParts` controls the extent to which the planner should infer predicates. [options="header",cols="2m,3a"] @@ -328,4 +328,4 @@ Avoiding the inference of multiple labels improves accuracy for nodes with sever // In general, inferring more information should improve the estimation and thereby the planner's decisions. // Should this not be the case, this setting provides the means to disable inference. -If this query option is not provided, then the value set in link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings/#config_dbms.cypher.infer_schema_parts[Operations Manual -> Configuration settings -> dbms.cypher.infer_schema_parts] will be used. +If this query option is not provided, then the value set in link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings/#config_dbms.cypher.infer_schema_parts[Operations Manual -> Configuration settings -> dbms.cypher.infer_schema_parts] will be used. diff --git a/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc b/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc index 060c5646f..d99788eaa 100644 --- a/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc +++ b/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc @@ -73,7 +73,7 @@ Planner COST Runtime SLOTTED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} +-------------------+----+------------------------------------------------------------------------+----------------+ | Operator | Id | Details | Estimated Rows | @@ -165,7 +165,7 @@ Planner COST Runtime PIPELINED -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 @@ -267,7 +267,7 @@ Planner COST Runtime PARALLEL -Runtime version {neo4j-version-minor} +Runtime version {neo4j-version} Batch size 128 diff --git a/modules/ROOT/pages/planning-and-tuning/runtimes/reference.adoc b/modules/ROOT/pages/planning-and-tuning/runtimes/reference.adoc index 57e009abc..f9de72cb0 100644 --- a/modules/ROOT/pages/planning-and-tuning/runtimes/reference.adoc +++ b/modules/ROOT/pages/planning-and-tuning/runtimes/reference.adoc @@ -27,7 +27,7 @@ For a full list of all available Cypher write clauses, see the xref:clauses/inde It is not possible to use the parallel runtime if a change has been made to the state of a transaction. -For example, the following transaction (initiated on link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/cypher-shell[Cypher Shell]) will be rolled back, because executing a Cypher query will make changes to the state of a transaction. +For example, the following transaction (initiated on link:{neo4j-docs-base-uri}/operations-manual/current/tools/cypher-shell[Cypher Shell]) will be rolled back, because executing a Cypher query will make changes to the state of a transaction. .Step 1: Initiate a new transaction and change its state by creating a node [source, cypher, role=test-skip] @@ -49,7 +49,7 @@ RETURN 42 An error occurred while in an open transaction. The transaction will be rolled back and terminated. Error: The parallel runtime is not supported if there are changes in the transaction state. Use another runtime. ---- -For more information about transactions in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/transaction-management[Operations Manual -> Transaction management]. +For more information about transactions in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/transaction-management[Operations Manual -> Transaction management]. [[configuration-settings]] == Configuration settings @@ -73,16 +73,16 @@ m| 0 Setting `server.cypher.parallel.worker_limit` to a negative number `-n` where `n` is greater than the total number of cores will disable the parallel runtime. -For more information about configuration settings in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration[Operations Manual -> Configuration.] +For more information about configuration settings in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/current/configuration[Operations Manual -> Configuration.] [[aura]] == Aura -The parallel runtime can only be run on Aura instances with 3 or more CPUs. -This means that the parallel runtime is not available to users to users of AuraDB Free. -Nor is it available on instances of AuraDB Professional and AuraDB Enterprise with 2 or fewer CPUs. +The parallel runtime is available on all non-free AuraDB instances, regardless of their size or CPU count. +Additionally, when a query is run with parallel runtime on an Aura instance, it can utilize up to the total number of available CPUs. -Attempting to run a query with the parallel runtime on instances with 2 or fewer CPUs will generate the following error message: +The parallel runtime is disabled on AuraDB Free instances. +Attempting to run a query with parallel runtime on AuraDB Free will throw the following error message: .Error message [source,error] @@ -92,7 +92,7 @@ Parallel runtime has been disabled, please enable it or upgrade to a bigger Aura [TIP] ==== -Users of AuraDB Professional and AuraDB Enterprise select the number of available CPUs when creating an instance. +Users of AuraDB Professional, AuraDB Business Critical, and AuraDB Virtual Dedicated Cloud select the the size and the number of available CPUs when creating an instance. More information about the various tiers of AuraDB can be found on the link:https://neo4j.com/pricing/[Neo4j Pricing page]. ==== @@ -103,7 +103,7 @@ Procedures and functions that read the database are supported by the parallel ru Apart from this, there are two categories of procedures and functions to keep in mind when using the parallel runtime. The first can be categorized as _updating procedures_. -These are procedures that update the graph with write queries, such as the Neo4j procedures link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_createlabel[db.createLabel] and link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_createproperty[db.createProperty]. +These are procedures that update the graph with write queries, such as the Neo4j procedures link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_createlabel[db.createLabel] and link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_createproperty[db.createProperty]. If such procedures are called in a query run on the parallel runtime, the query will fail. The second can be categorized as _non-thread-safe_ procedures and functions. @@ -129,66 +129,66 @@ Instead the query will automatically run on the pipelined runtime. | Procedure -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_awaitindex[db.awaitIndex] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_awaitindex[db.awaitIndex] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_awaitindexes[db.awaitIndexes] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_awaitindexes[db.awaitIndexes] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_checkpoint[db.checkpoint] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_checkpoint[db.checkpoint] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_info[db.info] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_info[db.info] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_labels[db.labels] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_labels[db.labels] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_listlocks[db.listLocks] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_listlocks[db.listLocks] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_ping[db.ping] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_ping[db.ping] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_propertykeys[db.propertyKeys] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_propertykeys[db.propertyKeys] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_prepareforreplanning[db.prepareForReplanning] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_prepareforreplanning[db.prepareForReplanning] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_relationshiptypes[db.relationshipTypes] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_relationshiptypes[db.relationshipTypes] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_resampleindex[db.resampleIndex] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_resampleindex[db.resampleIndex] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_resampleoutdatedindexes[db.resampleOutdatedIndexes] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_resampleoutdatedindexes[db.resampleOutdatedIndexes] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_schema_nodetypeproperties[db.schema.nodeTypeProperties] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_schema_nodetypeproperties[db.schema.nodeTypeProperties] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_schema_reltypeproperties[db.schema.relTypeProperties] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_schema_reltypeproperties[db.schema.relTypeProperties] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_schema_visualization[db.schema.visualization] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_db_schema_visualization[db.schema.visualization] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_checkconfigvalue[dbms.checkConfigValue] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_checkconfigvalue[dbms.checkConfigValue] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_listactivelocks[dbms.listActiveLocks] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_listactivelocks[dbms.listActiveLocks] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_listpools[dbms.listPools] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_listpools[dbms.listPools] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_scheduler_failedjobs[dbms.scheduler.failedJobs] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_scheduler_failedjobs[dbms.scheduler.failedJobs] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_scheduler_groups[dbms.scheduler.groups] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_scheduler_groups[dbms.scheduler.groups] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_scheduler_jobs[dbms.scheduler.jobs] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_scheduler_jobs[dbms.scheduler.jobs] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_upgrade[dbms.upgrade] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_upgrade[dbms.upgrade] -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_dbms_upgradestatus[dbms.upgradeStatus] +| link:{neo4j-docs-base-uri}/operations-manual/current/reference/procedures/#procedure_dbms_upgradestatus[dbms.upgradeStatus] |=== [[apoc]] === APOC -The link:{neo4j-docs-base-uri}/apoc/{page-version}/[APOC library] contains procedures and functions which extend the use of Cypher. +The link:{neo4j-docs-base-uri}/apoc/current/[APOC library] contains procedures and functions which extend the use of Cypher. There are a number of APOC procedures and functions that are not considered thread-safe, and *cannot* be run on the parallel runtime. -For information about these, refer to the pages of the individual link:{neo4j-docs-base-uri}/apoc/{page-version}/overview/[procedures and functions] in the APOC Manual. +For information about these, refer to the pages of the individual link:{neo4j-docs-base-uri}/apoc/current/overview/[procedures and functions] in the APOC Manual. [[user-defined-functions]] === User-defined functions User-defined functions are simpler forms of procedures that return a single value and are read-only. -To learn more about user-defined functions in Neo4j, see the link:{neo4j-docs-base-uri}/java-reference/{page-version}/extending-neo4j/functions/[Java Reference Manual -> User-defined functions]. +To learn more about user-defined functions in Neo4j, see the link:{neo4j-docs-base-uri}/java-reference/current/extending-neo4j/functions/[Java Reference Manual -> User-defined functions]. Similar to Neo4j and APOC procedures, any user-defined function that starts a new transaction by executing a Cypher query is not considered thread-safe and will not be supported by the parallel runtime (this includes all user-defined aggregating functions). diff --git a/modules/ROOT/pages/queries/basic.adoc b/modules/ROOT/pages/queries/basic.adoc index 530e50616..6876d75ba 100644 --- a/modules/ROOT/pages/queries/basic.adoc +++ b/modules/ROOT/pages/queries/basic.adoc @@ -678,7 +678,7 @@ RETURN type(r) AS type, m.title AS movie The result 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"] +image::introduction-example1.svg[Example graph connecting Tom Hanks to multiple Movie nodes,width=500,role=popup] .Result [role="queryresult",options="header,footer",cols="2* Syntax and semantics -> Variable length relationships]. +For more information, see xref::patterns/reference.adoc#variable-length-relationships[Patterns -> Syntax and semantics -> Variable-length relationships]. The xref:patterns/shortest-paths.adoc[`SHORTEST`] keyword can be used to find a variation of the shortest paths between two nodes. In this example, `ALL SHORTEST` paths between the two nodes `Keanu Reeves` and `Tom Cruise` are found. @@ -901,4 +901,6 @@ MATCH (n) DETACH DELETE n ---- -For more information, see the section on the xref:clauses/delete.adoc[] clause. +[NOTE] +`DETACH DELETE` is not suitable for deleting large amounts of data, nor does it delete xref:indexes/search-performance-indexes/overview.adoc[indexes] and xref:constraints/index.adoc[constraints]. +For more information, and alternatives to `DETACH DELETE`, see xref:clauses/delete.adoc#delete-all-nodes-and-relationships[`DELETE` -> Delete all nodes and relationships]. diff --git a/modules/ROOT/pages/queries/case.adoc b/modules/ROOT/pages/queries/case.adoc index 1dae13b99..09b67b128 100644 --- a/modules/ROOT/pages/queries/case.adoc +++ b/modules/ROOT/pages/queries/case.adoc @@ -15,7 +15,7 @@ Two variants of `CASE` exist within Cypher: the _simple_ form, to compare a sing The following graph is used for the examples below: -image:case_graph.svg[width="500",role="middle"] +image::case-graph.svg[Example graph connecting Person nodes,width=500,role=popup] To recreate the graph, run the following query against an empty Neo4j database: diff --git a/modules/ROOT/pages/query-caches/index.adoc b/modules/ROOT/pages/query-caches/index.adoc index fb2bde480..57cc4d906 100644 --- a/modules/ROOT/pages/query-caches/index.adoc +++ b/modules/ROOT/pages/query-caches/index.adoc @@ -16,7 +16,7 @@ For more information, see xref:query-caches/unified-query-caches.adoc[Unifying q == 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}/configuration/configuration-settings/[Operations Manual -> Configuration settings]. +For more information, see link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings/[Operations Manual -> Configuration settings]. .Query cache configurations [options="header", width="100%", cols="4m,3a,1m"] @@ -25,15 +25,15 @@ For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-ver | Description | Default -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings/#config_server.memory.query_cache.sharing_enabled[server.memory.query_cache.sharing_enabled] +| link:{neo4j-docs-base-uri}/operations-manual/current/configuration/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}/configuration/configuration-settings/#config_server.memory.query_cache.shared_cache_num_entries[server.memory.query_cache.shared_cache_num_entries] +| link:{neo4j-docs-base-uri}/operations-manual/current/configuration/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}/configuration/configuration-settings/#config_server.memory.query_cache.per_db_cache_num_entries[server.memory.query_cache.per_db_cache_num_entries] +| link:{neo4j-docs-base-uri}/operations-manual/current/configuration/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 diff --git a/modules/ROOT/pages/query-caches/unified-query-caches.adoc b/modules/ROOT/pages/query-caches/unified-query-caches.adoc index 74aa3f86a..20c7a1ca8 100644 --- a/modules/ROOT/pages/query-caches/unified-query-caches.adoc +++ b/modules/ROOT/pages/query-caches/unified-query-caches.adoc @@ -12,12 +12,12 @@ To enable the unified query caches, set the option `server.memory.query_cache.sh | Description | Default -| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings/#config_server.memory.query_cache.sharing_enabled[server.memory.query_cache.sharing_enabled] +| link:{neo4j-docs-base-uri}/operations-manual/current/configuration/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}/configuration/configuration-settings/#config_server.memory.query_cache.shared_cache_num_entries[server.memory.query_cache.shared_cache_num_entries] +| link:{neo4j-docs-base-uri}/operations-manual/current/configuration/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 diff --git a/modules/ROOT/pages/styleguide.adoc b/modules/ROOT/pages/styleguide.adoc index 0874703f7..bff296f2e 100644 --- a/modules/ROOT/pages/styleguide.adoc +++ b/modules/ROOT/pages/styleguide.adoc @@ -472,7 +472,7 @@ RETURN 'Cypher\'s a nice language', "Mats' quote: \"statement\"" RETURN "Cypher's a nice language", 'Mats\' quote: "statement"' ---- -* Avoid having to use back-ticks to escape characters and keywords. +* Avoid using characters and keywords that require the input to be quoted with backticks. .Bad [source, cypher] diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index 44d2001c9..30dbbb03a 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -1,5 +1,6 @@ = CALL subqueries :description: This page describes how to use Cypher's `CALL` subquery. +:page-aliases: clauses/call-subquery.adoc The `CALL` clause can be used to invoke subqueries that execute operations within a defined scope, thereby optimizing data handling and query efficiency. Unlike other subqueries in Cypher, `CALL` subqueries can be used to perform changes to the database (e.g. xref:clauses/create.adoc[] new nodes). @@ -30,14 +31,9 @@ CREATE (teamA:Team {name: 'Team A'}), (playerF:Player {name: 'Player F', age: 35}), (playerA)-[:PLAYS_FOR]->(teamA), (playerB)-[:PLAYS_FOR]->(teamA), - (playerC)-[:PLAYS_FOR]->(teamA), (playerD)-[:PLAYS_FOR]->(teamB), (playerE)-[:PLAYS_FOR]->(teamC), (playerF)-[:PLAYS_FOR]->(teamC), - (playerA)-[:FRIEND_OF]->(playerB), - (playerA)-[:FRIEND_OF]->(playerC), - (playerB)-[:FRIEND_OF]->(playerF), - (playerC)-[:FRIEND_OF]->(playerD), (teamA)-[:OWES {dollars: 1500}]->(teamB), (teamA)-[:OWES {dollars: 3000}]->(teamB), (teamB)-[:OWES {dollars: 1700}]->(teamC), @@ -92,7 +88,6 @@ CALL () { SET p.age = p.age + 1 RETURN p.age AS newAge } -WITH x, newAge MATCH (p:Player {name: 'Player A'}) RETURN x AS iteration, newAge, p.age AS totalAge ---- @@ -139,7 +134,7 @@ RETURN t AS team, players | players | (:Team {name: "Team A"}) -| [(:Player {name: "Player C", age: 19}), (:Player {name: "Player B", age: 23}), (:Player {name: "Player A", age: 24})] +| (:Player {name: "Player B", age: 23}), (:Player {name: "Player A", age: 24})] | (:Team {name: "Team B"}) | [(:Player {name: "Player D", age: 30})] @@ -432,60 +427,60 @@ Similar to xref:clauses/optional-match.adoc[`OPTIONAL MATCH`] any empty rows pro .Difference between using `CALL` and `OPTIONAL CALL` ==== -This example, which finds the friends of each `Player` and xref:functions/aggregating.adoc#functions-count[counts] the number of friends per player, highlights the difference between using `CALL` and `OPTIONAL CALL`. +This example, which finds the team that each `Player` plays for, highlights the difference between using `CALL` and `OPTIONAL CALL`. .Regular subquery `CALL` [source, cypher] ---- MATCH (p:Player) CALL (p) { - MATCH (p)-[:FRIEND_OF]->(friend:Player) - RETURN friend + MATCH (p)-[:PLAYS_FOR]->(team:Team) + RETURN team } -RETURN p.name AS playerName, count(friend) AS numberOfFriends -ORDER BY numberOfFriends +RETURN p.name AS playerName, team.name AS team ---- -.Optional subquery `CALL` +.Result [role="queryresult",options="header,footer",cols="2*m"] |=== -| playerName | numberOfFriends +| playerName | team -| "Player B" | 1 -| "Player C" | 1 -| "Player A" | 2 +| "Player A" | "Team A" +| "Player B" | "Team A" +| "Player D" | "Team B" +| "Player E" | "Team C" +| "Player F" | "Team C" -2+d|Rows: 3 +2+d|Rows: 5 |=== -Note that no results are returned for `Player D`, `Player E`, and `Player F`, since they have no outgoing `FRIEND_OF` relationships connected to them. +Note that no results are returned for `Player C`, since they are not connected to any `Team` with a `PLAYS_FOR` relationship. .Query using regular `OPTIONAL CALL` [source, cypher] ---- MATCH (p:Player) OPTIONAL CALL (p) { - MATCH (p)-[:FRIEND_OF]->(friend:Player) - RETURN friend + MATCH (p)-[:PLAYS_FOR]->(team:Team) + RETURN team } -RETURN p.name AS playerName, count(friend) AS numberOfFriends -ORDER BY numberOfFriends +RETURN p.name AS playerName, team.name AS team ---- -Now, all `Player` nodes, regardless of whether they have any friends or not, are returned. -(Those without any outgoing `FRIEND_OF` relationships are returned with the result `0` because `count()` ignores `null` values.) +Now all `Player` nodes, regardless of whether they have any `PLAYS_FOR` relationships connected to a `Team`, are returned. +.Result .Result [role="queryresult",options="header,footer",cols="2*m"] |=== -| playerName | numberOfFriends +| playerName | team -| "Player D" | 0 -| "Player E" | 0 -| "Player F" | 0 -| "Player B" | 1 -| "Player C" | 1 -| "Player A" | 2 +| "Player A" | "Team A" +| "Player B" | "Team A" +| "Player C" | NULL +| "Player D" | "Team B" +| "Player E" | "Team C" +| "Player F" | "Team C" 2+d|Rows: 6 |=== @@ -495,8 +490,8 @@ Now, all `Player` nodes, regardless of whether they have any friends or not, are [[call-execution-order]] == Execution order of CALL subqueries -The order in which subqueries are executed is not defined. -If a query result depends on the order of execution of subqueries, an `ORDER BY` clause should precede the `CALL` clause. +The order in which rows from the outer scope are passed into subqueries is not defined. +If the results of the subquery depend on the order of these rows, use an `ORDER BY` clause before the `CALL` clause to guarantee a specific processing order for the rows. .Ordering results before `CALL` subquery ==== @@ -590,13 +585,13 @@ UNION ORDER BY p.age DESC LIMIT 1 } -RETURN p.name AS name, p.age AS age +RETURN p.name AS playerName, p.age AS age ---- .Result [role="queryresult",options="header,footer",cols="2*(p2:Player) - RETURN p2.name AS friend + MATCH (p)-[:PLAYS_FOR]->(team:Team) + RETURN team.name AS team } -RETURN p.name AS player, friend +RETURN p.name AS playerName, team ---- .Result -[role="queryresult",options="header,footer",cols="2* Concurrent data access]. +A deadlock happens when two transactions are blocked by each other because they are attempting to concurrently modify a node or a relationship that is locked by the other transaction (for more information about locks and deadlocks in Neo4j, see link:{neo4j-docs-base-uri}/operations-manual/current/database-internals/concurrent-data-access/#_locks[Operations Manual -> Concurrent data access]. A deadlock may occur when using `CALL { ... } IN CONCURRENT TRANSACTIONS` if the transactions for two or more batches try to take the same locks in an order that results in a circular dependency between them. If so, the impacted transactions are always rolled back, and an error is thrown unless the query is appended with `ON ERROR CONTINUE` or `ON ERROR BREAK`. @@ -665,7 +665,7 @@ If so, the impacted transactions are always rolled back, and an error is thrown ==== The following query tries to create `Movie` and `Year` nodes connected by a `RELEASED_IN` relationship. -Note that there are only three different years in the CSV file, meaning hat only three `Year` nodes should be created. +Note that there are only three different years in the CSV file, meaning that only three `Year` nodes should be created. .Query with concurrent transaction causing a deadlock [source, cypher, role=test-fail] diff --git a/modules/ROOT/pages/syntax/index.adoc b/modules/ROOT/pages/syntax/index.adoc index 498839606..04a664be0 100644 --- a/modules/ROOT/pages/syntax/index.adoc +++ b/modules/ROOT/pages/syntax/index.adoc @@ -8,7 +8,7 @@ Further information can be found in the following sections: * xref::syntax/parsing.adoc[Parsing] * xref::syntax/naming.adoc[Naming rules and recommendations] * xref::syntax/variables.adoc[Variables] -* xref::syntax/reserved.adoc[Reserved keywords] +* xref::syntax/keywords.adoc[Keywords] * xref::syntax/parameters.adoc[Parameters] * xref::syntax/operators.adoc[Operators] * xref::syntax/comments.adoc[Comments] diff --git a/modules/ROOT/pages/syntax/keywords.adoc b/modules/ROOT/pages/syntax/keywords.adoc new file mode 100644 index 000000000..4e7a24477 --- /dev/null +++ b/modules/ROOT/pages/syntax/keywords.adoc @@ -0,0 +1,395 @@ +:description: This section contains a list of reserved keywords in Cypher. +:page-aliases: syntax/reserved.adoc + +[[cypher-keywords]] += Keywords + +Keywords are words with a predefined meaning in Cypher. + +Keywords are not recommended to be used as identifiers in the following contexts: + +* Variables +* Labels +* Relationship types +* Function names +* Procedure names + +If a snippet of Cypher can both be a keyword and an unquoted identifier, it is interpreted as a keyword. + +For instance in the following, `true` could be the variable `true` or the Boolean literal value `true`. Since `true` is a keyword, the query returns a single Boolean `true` value. + +.Query +[source, cypher, role=test-result-skip] +---- +WITH 123 AS true +RETURN true AS x +---- + +.Result +[role="queryresult",options="header,footer",cols="1*+ | +"London"+ -3+d|Rows: 1 + -Nodes created: 1 + -Properties set: 5 + -Labels added: 1 + +3+d|Rows: 1 |=== See xref::clauses/set.adoc#set-replace-properties-using-map[Replace all properties using a map and `=`] for more details on using the property replacement operator `=`. @@ -194,10 +186,8 @@ The properties on the node are updated as follows by those provided in the map: |=== | +p.name+ | +p.age+ | +p.livesIn+ | +"Ellen"+ | +20+ | +"London"+ -3+d|Rows: 1 + -Nodes created: 1 + -Properties set: 4 + -Labels added: 1 + +3+d|Rows: 1 |=== See xref::clauses/set.adoc#set-setting-properties-using-map[Mutate specific properties using a map and `+=`] for more details on using the property mutation operator `+=`. @@ -250,6 +240,7 @@ RETURN b - a AS result |=== | +result+ | +7+ + 1+d|Rows: 1 |=== @@ -292,6 +283,7 @@ RETURN one > two AS result |=== | +result+ | +true+ + 1+d|Rows: 1 |=== @@ -317,6 +309,7 @@ RETURN candidate | +candidate+ | +"John"+ | +"Jonathan"+ + 1+d|Rows: 2 |=== @@ -531,6 +524,7 @@ RETURN number | +4+ | +7+ | +9+ + 1+d|Rows: 3 |=== @@ -560,6 +554,7 @@ RETURN 'neo' + '4j' AS result |=== | +result+ | +"neo4j"+ + 1+d|Rows: 1 |=== @@ -605,7 +600,8 @@ RETURN "the \u212B char" IS NORMALIZED AS normalized |=== | normalized | false -2+|Rows: 1 + +1+|Rows: 1 |=== Because the given `STRING` contains a non-normalized Unicode character (`\u212B`), `false` is returned. @@ -632,7 +628,8 @@ RETURN "the \u212B char" IS NOT NORMALIZED AS notNormalized |=== | notNormalized | true -2+|Rows: 1 + +1+|Rows: 1 |=== Because the given `STRING` contains a non-normalized Unicode character (`\u212B`), and is not normalized, `true` is returned. @@ -752,6 +749,7 @@ RETURN aDateTime + aDuration, aDateTime - aDuration |=== | +aDateTime + aDuration+ | +aDateTime - aDuration+ | +1996-10-11T12:31:14.000000002+ | +1972-10-11T12:31:13.999999998+ + 2+d|Rows: 1 |=== @@ -791,6 +789,7 @@ RETURN |=== | +date1+ | +date2+ | +2012-02-28+ | +2012-02-29+ + 2+d|Rows: 1 |=== @@ -812,6 +811,7 @@ RETURN duration1, duration2, duration1 + duration2, duration1 - duration2 |=== | +duration1+ | +duration2+ | +duration1 + duration2+ | +duration1 - duration2+ | +P12Y5M14DT16H13M10.000000001S+ | +P1M-14DT15H49M10S+ | +P12Y6MT32H2M20.000000001S+ | +P12Y4M28DT24M0.000000001S+ + 4+d|Rows: 1 |=== @@ -833,6 +833,7 @@ RETURN aDuration, aDuration * 2, aDuration / 3 |=== | +aDuration+ | +aDuration * 2+ | +aDuration / 3+ | +P14DT13M10.000000001S+ | +P28DT26M20.000000002S+ | +P4DT16H4M23.333333333S+ + 3+d|Rows: 1 |=== @@ -867,6 +868,7 @@ RETURN p.person.name |=== | +p.person.name+ | +"Anne"+ + 1+d|Rows: 1 |=== @@ -896,6 +898,7 @@ RETURN a[$myKey] AS result |=== | +result+ | +"Anne"+ + 1+d|Rows: 1 |=== @@ -930,6 +933,7 @@ RETURN [1,2,3,4,5] + [6,7] AS myList |=== | +myList+ | +[1,2,3,4,5,6,7]+ + 1+d|Rows: 1 |=== @@ -948,6 +952,7 @@ RETURN [1,2,3,4,5] || [6,7] AS myList |=== | myList | [1,2,3,4,5,6,7] + 1+d|Rows: 1 |=== @@ -971,6 +976,7 @@ RETURN number | +number+ | +2+ | +3+ + 1+d|Rows: 2 |=== @@ -997,6 +1003,7 @@ If the left-hand operator had been `[1, 2]` instead of `[2, 1]`, the query would |=== | +inList+ | +true+ + 1+d|Rows: 1 |=== @@ -1015,6 +1022,7 @@ However, `IN` evaluates to `false` as the right-hand operand does not contain an |=== | +inList+ | +false+ + 1+d|Rows: 1 |=== @@ -1047,6 +1055,7 @@ The square brackets will extract the elements from the start index `1`, and up t |=== | +result+ | +["John","Bill"]+ + 1+d|Rows: 1 |=== @@ -1076,6 +1085,7 @@ RETURN names[$myIndex] AS result |=== | +result+ | +"John"+ + 1+d|Rows: 1 |=== @@ -1097,6 +1107,7 @@ RETURN 3 IN l[0] AS result |=== | +result+ | +true+ + 1+d|Rows: 1 |=== diff --git a/modules/ROOT/pages/syntax/parameters.adoc b/modules/ROOT/pages/syntax/parameters.adoc index 41a006e10..f37a97fb4 100644 --- a/modules/ROOT/pages/syntax/parameters.adoc +++ b/modules/ROOT/pages/syntax/parameters.adoc @@ -15,14 +15,16 @@ Additionally, parameters make caching of execution plans much easier for Cypher, Parameters can be used for: -* literals and expressions -* node and relationship ids +* Literals and expressions. +* Node and relationship ids. +* Properties, when referenced dynamically (for more information, see xref:clauses/where.adoc#filter-on-dynamic-property[Filter on dynamically-computed node property]). +* Node labels and relationship types, when referenced dynamically (for more information, see xref:clauses/match.adoc#dynamic-match[`MATCH` using dynamic node labels and relationship types]). label:new[Introduced in 5.26] Parameters cannot be used for the following constructs, as these form part of the query structure that is compiled into a query plan: -* property keys; so `MATCH (n) WHERE n.$param = 'something'` is invalid -* relationship types; so `MATCH (n)-[:$param]->(m)` is invalid -* labels; so `MATCH (n:$param)` is invalid +* Property keys; `MATCH (n) WHERE n.$param = 'something'` is invalid. +* Relationship types; `MATCH (n)-[:$param]->(m)` is invalid. +* Node labels; `MATCH (n:$param)` is invalid. Parameters may consist of letters and numbers, and any combination of these, but cannot start with a number or a currency symbol. @@ -30,11 +32,11 @@ Setting parameters when running a query is dependent on the client environment. For example: * To set a parameter in Cypher Shell use `+:param name => 'Joe'+`. - For more information refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/cypher-shell#cypher-shell-parameters[Operations Manual -> Cypher Shell - Query Parameters]. + For more information refer to link:{neo4j-docs-base-uri}/operations-manual/current/tools/cypher-shell#cypher-shell-parameters[Operations Manual -> Cypher Shell - Query Parameters]. * For Neo4j Browser use the same syntax as Cypher Shell, `+:param name => 'Joe'+`. * When using drivers, the syntax is dependent on the language choice. See the examples in _Transactions_ in the link:{docs-base-uri}[Neo4j Driver manuals]. -* For usage via the Neo4j HTTP API, see the link:{neo4j-docs-base-uri}/http-api/{page-version}/index#http-api[HTTP API documentation]. +* For usage via the Neo4j HTTP API, see the link:{neo4j-docs-base-uri}/http-api/current/index#http-api[HTTP API documentation]. We provide below a comprehensive list of examples of parameter usage. In these examples, parameters are given in JSON; the exact manner in which they are to be submitted depends upon the driver being used. diff --git a/modules/ROOT/pages/syntax/parsing.adoc b/modules/ROOT/pages/syntax/parsing.adoc index 54db83bde..66887f583 100644 --- a/modules/ROOT/pages/syntax/parsing.adoc +++ b/modules/ROOT/pages/syntax/parsing.adoc @@ -6,10 +6,20 @@ This page provides a general overview of how Cypher parses an 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. +This page details the general rules on which characters are considered valid input. == Using unicodes in Cypher + Unicodes can generally be escaped as `\uxxx`. +For example, the below query uses the Unicode `u00B0` to search for any recipe descriptions containing the degree symbol, `º`: + +.Using Unicodes in `STRING` matching +[source, cypher] +---- +MATCH (r:Recipe) +WHERE r.description CONTAINS "\u00B0" +RETURN r +---- Additional documentation on escaping rules for `STRING` literals, names and regular expressions can be found here: @@ -17,13 +27,6 @@ Additional documentation on escaping rules for `STRING` literals, names and regu * xref::syntax/naming.adoc#symbolic-names-escaping-rules[Using special characters in names] * xref::clauses/where.adoc#escaping-in-regular-expressions[Regular expressions] -The following example escapes the unicode character `A` (`\u0041`) in the keyword `MATCH`: - -[source, syntax] ----- -M\u0041TCH (m) RETURN m; ----- - The Unicode version used by Cypher depends on the running JVM version. [options="header", cols="1,2,3"] diff --git a/modules/ROOT/pages/syntax/reserved.adoc b/modules/ROOT/pages/syntax/reserved.adoc deleted file mode 100644 index e38ba0ed2..000000000 --- a/modules/ROOT/pages/syntax/reserved.adoc +++ /dev/null @@ -1,117 +0,0 @@ -:description: This section contains a list of reserved keywords in Cypher. - -[[cypher-reserved]] -= Reserved keywords - - -This page contains a list of reserved keywords in Cypher. - -Reserved keywords are words that have a special meaning in Cypher. -The listing of the reserved keywords are grouped by the categories from which they are drawn. -In addition to this, there are a number of keywords that are reserved for future use. - -The reserved keywords are not permitted to be used as identifiers in the following contexts: - -* Variables -* Function names -* Parameters - -If any reserved keyword is escaped -- i.e. is encapsulated by backticks ```, such as `++`AND`++` -- it would become a valid identifier in the above contexts. - -== Clauses - -* `CALL` -* `CREATE` -* `DELETE` -* `DETACH` -* `FOREACH` -* `LOAD` -* `MATCH` -* `MERGE` -* `OPTIONAL` -* `REMOVE` -* `RETURN` -* `SET` -* `START` -* `UNION` -* `UNWIND` -* `WITH` - -== Subclauses - -* `LIMIT` -* `ORDER` -* `SKIP` -* `WHERE` -* `YIELD` - -== Modifiers - -* `ASC` -* `ASCENDING` -* `ASSERT` -* `BY` -* `CSV` -* `DESC` -* `DESCENDING` -* `ON` - -== Expressions - -* `ALL` -* `CASE` -* `COUNT` -* `ELSE` -* `END` -* `EXISTS` -* `THEN` -* `WHEN` - -== Operators - -* `AND` -* `AS` -* `CONTAINS` -* `DISTINCT` -* `ENDS` -* `IN` -* `IS` -* `NOT` -* `OR` -* `STARTS` -* `XOR` - -== Schema - -* `CONSTRAINT` -* `CREATE` -* `DROP` -* `EXISTS` -* `INDEX` -* `NODE` -* `KEY` -* `UNIQUE` - -== Hints - -* `INDEX` -* `JOIN` -* `SCAN` -* `USING` - -== Literals - -* `false` -* `null` -* `true` - -== Reserved for future use - -* `ADD` -* `DO` -* `FOR` -* `MANDATORY` -* `OF` -* `REQUIRE` -* `SCALAR` - diff --git a/modules/ROOT/pages/syntax/variables.adoc b/modules/ROOT/pages/syntax/variables.adoc index 2f4234691..9c4096199 100644 --- a/modules/ROOT/pages/syntax/variables.adoc +++ b/modules/ROOT/pages/syntax/variables.adoc @@ -24,7 +24,13 @@ Information regarding the naming of variables may be found xref::syntax/naming.a .Variables are only visible in the same query part ==== Variables are not carried over to subsequent queries. -If multiple query parts are chained together using `WITH`, variables have to be listed in the `WITH` clause to be carried over to the next part. +If multiple query parts are chained together using `WITH`, variables defined in one part have to be listed in the `WITH` clause to be carried over to the next part. For more information see xref::clauses/with.adoc[WITH]. ==== +[IMPORTANT] +.Variables imported into a `CALL` subquery are visible in the whole subquery +==== +Even if the subquery consists of multiple query parts chained together using `WITH`, variables imported from the outer query do not have to be listed in a `WITH` clause to be visible in subsequent parts of the subquery. +For more information see xref::subqueries/call-subquery.adoc#variable-scope-clause[The variable scope clause]. +==== diff --git a/modules/ROOT/pages/values-and-types/maps.adoc b/modules/ROOT/pages/values-and-types/maps.adoc index 8d50aae27..a3e005a04 100644 --- a/modules/ROOT/pages/values-and-types/maps.adoc +++ b/modules/ROOT/pages/values-and-types/maps.adoc @@ -17,7 +17,7 @@ The behavior of the `[]` operator with respect to `null` is detailed xref::value == Literal maps The key names in a map must be literals. -If returned through an link:{neo4j-docs-base-uri}/http-api/{page-version}[HTTP API call], a JSON object will be returned. +If returned through an link:{neo4j-docs-base-uri}/http-api/current[HTTP API call], a JSON object will be returned. If returned in Java, an object of type `java.util.Map` will be returned. diff --git a/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc b/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc index 618ade925..6b4e1657b 100644 --- a/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc +++ b/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc @@ -13,7 +13,7 @@ This section will first provide a brief overview of each type, and then go into A property type value is one that can be stored as a node or relationship property. -The following data types are included in the property types category: `BOOLEAN`, `DATE`, `DURATION`, `FLOAT`, `INTEGER`, `LIST`, `LOCAL DATETIME`, `LOCAL TIME`, `POINT`, `STRING`, `ZONED DATETIME`, and `ZONED TIME`. +Property types are the most primitive types in Cypher and include the following: `BOOLEAN`, `DATE`, `DURATION`, `FLOAT`, `INTEGER`, `LIST`, `LOCAL DATETIME`, `LOCAL TIME`, `POINT`, `STRING`, `ZONED DATETIME`, and `ZONED TIME`. * Property types can be returned from Cypher queries. * Property types can be used as xref::syntax/parameters.adoc[parameters]. @@ -74,7 +74,7 @@ For more details, see xref::values-and-types/working-with-null.adoc[working with The table below shows the types and their syntactic synonyms. -These types (and their synonyms) can be used in xref::values-and-types/type-predicate.adoc[type predicate expressions] and in xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. +These types (and their synonyms) can be used in xref::values-and-types/type-predicate.adoc[type predicate expressions] and in xref::constraints/managing-constraints.adoc#create-property-type-constraints[property type constraints]. They are also returned as a `STRING` value when using the xref::functions/scalar.adoc#functions-valueType[valueType()] function. However, not all types can be used in all places. @@ -121,7 +121,7 @@ The type `PROPERTY VALUE` is expanded to a closed dynamic union of all valid pro For example, given the closed dynamic type `BOOL | LIST | BOOLEAN | LIST`, the normalized type would be: `BOOLEAN | LIST`. -This normalization is run on types used in xref::values-and-types/type-predicate.adoc[type predicate expressions], and in xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. +This normalization is run on types used in xref::values-and-types/type-predicate.adoc[type predicate expressions], and in xref::constraints/managing-constraints.adoc#create-property-type-constraints[property type constraints]. Type normalization is also used to ensure the consistency of the output for the xref::functions/scalar.adoc#functions-valueType[valueType()] function. [[ordering-of-types]] diff --git a/modules/ROOT/pages/values-and-types/temporal.adoc b/modules/ROOT/pages/values-and-types/temporal.adoc index bba317da5..106107711 100644 --- a/modules/ROOT/pages/values-and-types/temporal.adoc +++ b/modules/ROOT/pages/values-and-types/temporal.adoc @@ -75,7 +75,7 @@ See xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[speci The named time zone form uses the rules of the IANA time zone database to manage _daylight savings time_ (DST). -The default time zone of the database can be configured using the configuration option link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings#config_db.temporal.timezone[`db.temporal.timezone`]. +The default time zone of the database can be configured using the configuration option link:{neo4j-docs-base-uri}/operations-manual/current/configuration/configuration-settings#config_db.temporal.timezone[`db.temporal.timezone`]. This configuration option influences the creation of temporal types for the following functions: * Getting the current date and time without specifying a time zone. @@ -505,153 +505,403 @@ For more information, see the xref::values-and-types/temporal.adoc#cypher-tempor | | | -| - |=== [[cypher-temporal-specify-instant-examples]] === Examples -Below are examples of parsing instant values using various temporal functions. -More information about these temporal functions can be found xref::functions/temporal/index.adoc[here]. +To work with a particular temporal instant type, its corresponding xref:functions/temporal/index.adoc[function] must be used. +For example, in order to create a property value of type `ZONED DATETIME`, the xref:functions/temporal/index.adoc#functions-datetime[`datetime()`] function must be used. -.+datetime+ -====== +For specific examples, see: -Parsing a `ZONED DATETIME` using the _calendar date_ format: +* xref:values-and-types/temporal.adoc#examples-date[`DATE`] +* xref:values-and-types/temporal.adoc#examples-localtime[`LOCAL TIME`] +* xref:values-and-types/temporal.adoc#examples-zonedtime[`ZONED TIME`] +* xref:values-and-types/temporal.adoc#examples-local-datetime[`LOCAL DATETIME`] +* xref:values-and-types/temporal.adoc#examples-zoned-datetime[`ZONED DATETIME`] +* xref:values-and-types/temporal.adoc#examples-truncate[Truncating temporal values] -.Query +[[examples-date]] +==== `DATE` + +To work with `DATE` values, including creating, parsing, and extracting components, use the xref:functions/temporal/index.adoc#functions-date[`date()`] function. + +.`DATE` +====== + +.Create a `DATE` property value [source, cypher] ---- -RETURN datetime('2015-06-24T12:50:35.556+0100') AS theDateTime +CREATE (n:Label) +SET n.date = date("2025-02-18") +RETURN n.date AS date, valueType(n.date) AS temporalValueType ---- .Result -[role="queryresult",options="header,footer",cols="1*=16.0.0" } }, "node_modules/@antora/expand-path-helper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@antora/expand-path-helper/-/expand-path-helper-2.0.0.tgz", - "integrity": "sha512-CSMBGC+tI21VS2kGW3PV7T2kQTM5eT3f2GTPVLttwaNYbNxDve08en/huzszHJfxo11CcEs26Ostr0F2c1QqeA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@antora/expand-path-helper/-/expand-path-helper-3.0.0.tgz", + "integrity": "sha512-7PdEIhk97v85/CSm3HynCsX14TR6oIVz1s233nNLsiWubE8tTnpPt4sNRJR+hpmIZ6Bx9c6QDp3XIoiyu/WYYA==", "engines": { - "node": ">=10.17.0" + "node": ">=16.0.0" } }, "node_modules/@antora/file-publisher": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.9.tgz", - "integrity": "sha512-C0VwVjuFbE1CVpZDgwYR1gZCNr1tMw5vueyF9wHZH0KCqAsp9iwo7bwj8wKWMPogxcxdYhnAvtDJnYmYFCuDWQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.10.tgz", + "integrity": "sha512-DPR/0d1P+kr3qV4T0Gh81POEO/aCmNWIp/oLUYAhr0HHOcFzgpTUUoLStgcYynZPFRIB7EYKSab+oYSCK17DGA==", "dependencies": { - "@antora/expand-path-helper": "~2.0", - "@antora/user-require-helper": "~2.0", + "@antora/expand-path-helper": "~3.0", + "@antora/user-require-helper": "~3.0", "vinyl": "~3.0", "yazl": "~2.5" }, @@ -139,11 +139,11 @@ } }, "node_modules/@antora/logger": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.9.tgz", - "integrity": "sha512-MKuANodcX0lfRyiB+Rxl/Kv7UOxc2glzTYFoIoBB7uzxF0A+AhvUJDmpGQFRFN2ihxy99N3nLJmZpDebwXyE+A==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.10.tgz", + "integrity": "sha512-WSuIxEP2tVrhWtTj/sIrwBDjpi4ldB/1Kpiu4PXmY4/qeWP8thW6u8nXdwdDcWss5zqkZWjourvWKwVq7y8Wjg==", "dependencies": { - "@antora/expand-path-helper": "~2.0", + "@antora/expand-path-helper": "~3.0", "pino": "~9.2", "pino-pretty": "~11.2", "sonic-boom": "~4.0" @@ -153,22 +153,22 @@ } }, "node_modules/@antora/navigation-builder": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.9.tgz", - "integrity": "sha512-zyl2yNjK31Dl6TRJgnoFb4Czwt9ar3wLTycAdMeZ+U/8YcAUHD8z7NCssPFFvZ0BbUr00NP+gbqDmCr6yz32NQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.10.tgz", + "integrity": "sha512-aLMK49nYsSB3mEZbLkmUXDAUYmscv2AFWu+5c3eqVGkQ6Wgyd79WQ6Bz3/TN9YqkzGL+PqGs0G39F0VQzD23Hw==", "dependencies": { - "@antora/asciidoc-loader": "3.1.9" + "@antora/asciidoc-loader": "3.1.10" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/page-composer": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.9.tgz", - "integrity": "sha512-X6Qj+J5dfFAGXoCAOaA+R6xRp8UoNMDHsRsB1dUTT2QNzk1Lrq6YkYyljdD2cxkWjLVqQ/pQSP+BJVNFGbqDAQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.10.tgz", + "integrity": "sha512-JoEg8J8HVsnPmAgUrYSGzf0C8rQefXyCi/18ucy0utyfUvlJNsZvUbGUPx62Het9p0JP0FkAz2MTLyDlNdArVg==", "dependencies": { - "@antora/logger": "3.1.9", + "@antora/logger": "3.1.10", "handlebars": "~4.7", "require-from-string": "~2.0" }, @@ -177,9 +177,9 @@ } }, "node_modules/@antora/playbook-builder": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.9.tgz", - "integrity": "sha512-MJ/OWz4pReC98nygGTXC5bOL/TDDtCYpSkHFBz2ST4L6tuM8rv9c5+cp//JkwY/QlTOvcuJ0f2xq4a7a5nI7Qw==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.10.tgz", + "integrity": "sha512-UB8UmRYfkKgActTUlotdVS4FKGjaZgTnSXE7Fns1xb3/3HRanWvI+Yze1OmCkGC33cTpoQFnSYp7ySEH8LaiBw==", "dependencies": { "@iarna/toml": "~2.2", "convict": "~6.2", @@ -191,9 +191,9 @@ } }, "node_modules/@antora/redirect-producer": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.9.tgz", - "integrity": "sha512-9OLwoMhqifsBxTebInh/5W16GdDsdj+YkKG3TiCASlAOYsDbuhbeRPFUlyKKSRkMrtKKnFgHR0Z3DNPXYlH2NQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.10.tgz", + "integrity": "sha512-IbWJGh6LmsxJQ821h0B9JfooofFZBgFLZxsbp/IoTLkBFGLFAY5tDRvB6rvubfNLRoSjM8VjEUXGqVLlwZOb+g==", "dependencies": { "vinyl": "~3.0" }, @@ -202,46 +202,46 @@ } }, "node_modules/@antora/site-generator": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.9.tgz", - "integrity": "sha512-YYESPG22tGX1CxRPSAr6acKILCO8JfGkM1OYc7Sw3D7ZvCy1YgZMAaTYK0T5yl9LXg+l/UZi1xq/Ej0qHnYQiw==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.10.tgz", + "integrity": "sha512-NCULYtwUjIyr5FGCymhfG/zDVUmZ6pfmCPorka8mAzo4/GDx1T7bgaRL9rEIyf2AMqcm7apQiAz03mpU4kucsw==", "dependencies": { - "@antora/asciidoc-loader": "3.1.9", - "@antora/content-aggregator": "3.1.9", - "@antora/content-classifier": "3.1.9", - "@antora/document-converter": "3.1.9", - "@antora/file-publisher": "3.1.9", - "@antora/logger": "3.1.9", - "@antora/navigation-builder": "3.1.9", - "@antora/page-composer": "3.1.9", - "@antora/playbook-builder": "3.1.9", - "@antora/redirect-producer": "3.1.9", - "@antora/site-mapper": "3.1.9", - "@antora/site-publisher": "3.1.9", - "@antora/ui-loader": "3.1.9", - "@antora/user-require-helper": "~2.0" + "@antora/asciidoc-loader": "3.1.10", + "@antora/content-aggregator": "3.1.10", + "@antora/content-classifier": "3.1.10", + "@antora/document-converter": "3.1.10", + "@antora/file-publisher": "3.1.10", + "@antora/logger": "3.1.10", + "@antora/navigation-builder": "3.1.10", + "@antora/page-composer": "3.1.10", + "@antora/playbook-builder": "3.1.10", + "@antora/redirect-producer": "3.1.10", + "@antora/site-mapper": "3.1.10", + "@antora/site-publisher": "3.1.10", + "@antora/ui-loader": "3.1.10", + "@antora/user-require-helper": "~3.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/site-generator-default": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.9.tgz", - "integrity": "sha512-m/QCv2o/24VmWZaeqtc6nNEky///GTLLx/pyyihP7uWKvZ0AhGPp6Agv1yaShjKIthBzHJ3JozaMPev2leor+A==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.10.tgz", + "integrity": "sha512-dMhjbklthysj3espwYNkTkADm2Z3EbWThq9gJv/ZuSXGZSXVSwt8b3mBpCTwxOeAKIldnj3fc1pzQxei/7PC2w==", "dependencies": { - "@antora/site-generator": "3.1.9" + "@antora/site-generator": "3.1.10" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/site-mapper": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.9.tgz", - "integrity": "sha512-9FCObL+JIjBoby8z+beu2uuvAtCjm5EsEQt+16gCIMX1ktVP3W3gVsdRSvVcGcVEpizILFhMawkcQknZPUp5mg==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.10.tgz", + "integrity": "sha512-KY1j/y0uxC2Y7RAo4r4yKv9cgFm8aZoRylZXEODJnwj3tffbZ2ZdRzSWHp6fN0QX/Algrr9JNd9CWrjcj2f3Zw==", "dependencies": { - "@antora/content-classifier": "3.1.9", + "@antora/content-classifier": "3.1.10", "vinyl": "~3.0" }, "engines": { @@ -249,22 +249,22 @@ } }, "node_modules/@antora/site-publisher": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.9.tgz", - "integrity": "sha512-L5To8f4QswZliXu6yB6O7O8CuBbLctjNbxZqP3m0FP7VaOONp85ftzEq1BFEm4BXXSwH1n4ujZx1qGBHP9ooOQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.10.tgz", + "integrity": "sha512-G4xcUWvgth8oeEQwiu9U1cE0miQtYHwKHOobUbDBt2Y6LlC5H31zQQmAyvMwTsGRlvYRgLVtG6j9d6JBwQ6w9Q==", "dependencies": { - "@antora/file-publisher": "3.1.9" + "@antora/file-publisher": "3.1.10" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/ui-loader": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.9.tgz", - "integrity": "sha512-g0/9dRE5JVMYukIU3x+Rvr41bPdK3sUD2xQIAniRjE6usIZs1mEsTGshVKVEoOqqnSekXE85HVhybjNHsC+qbQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.10.tgz", + "integrity": "sha512-H1f5wI5a5HjLuE/Wexvc8NZy8w83Bhqjka7t1DbwOOqP+LyxFGLx/QbBVKdTtgFNDHVMtNBlplQq0ixeoTSh0A==", "dependencies": { - "@antora/expand-path-helper": "~2.0", + "@antora/expand-path-helper": "~3.0", "braces": "~3.0", "cache-directory": "~2.0", "fast-glob": "~3.3", @@ -292,20 +292,21 @@ } }, "node_modules/@antora/user-require-helper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@antora/user-require-helper/-/user-require-helper-2.0.0.tgz", - "integrity": "sha512-5fMfBZfw4zLoFdDAPMQX6Frik90uvfD8rXOA4UpXPOUikkX4uT1Rk6m0/4oi8oS3fcjiIl0k/7Nc+eTxW5TcQQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@antora/user-require-helper/-/user-require-helper-3.0.0.tgz", + "integrity": "sha512-KIXb8WYhnrnwH7Jj21l1w+et9k5GvcgcqvLOwxqWLEd0uVZOiMFdqFjqbVm3M+zcrs1JXWMeh2LLvxBbQs3q/Q==", "dependencies": { - "@antora/expand-path-helper": "~2.0" + "@antora/expand-path-helper": "~3.0" }, "engines": { - "node": ">=10.17.0" + "node": ">=16.0.0" } }, "node_modules/@asciidoctor/core": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.6.tgz", - "integrity": "sha512-TmB2K5UfpDpSbCNBBntXzKHcAk2EA3/P68jmWvmJvglVUdkO9V6kTAuXVe12+h6C4GK0ndwuCrHHtEVcL5t6pQ==", + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.8.tgz", + "integrity": "sha512-oozXk7ZO1RAd/KLFLkKOhqTcG4GO3CV44WwOFg2gMcCsqCUTarvMT7xERIoWW2WurKbB0/ce+98r01p8xPOlBw==", + "license": "MIT", "dependencies": { "asciidoctor-opal-runtime": "0.3.3", "unxhr": "1.0.1" @@ -322,14 +323,19 @@ "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, "node_modules/@neo4j-antora/antora-add-notes": { - "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==" + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-add-notes/-/antora-add-notes-0.3.2.tgz", + "integrity": "sha512-Jsv17dEBELSkqplEIZE9b5I2zjYPvoHi4momLRt1FfBRQnBTWbk4kkf2JQojRJ8mQEVscj2tApfTDOAUtAOSLA==", + "license": "MIT" }, "node_modules/@neo4j-antora/antora-modify-sitemaps": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-modify-sitemaps/-/antora-modify-sitemaps-0.4.4.tgz", - "integrity": "sha512-IkXoilOJquZPB5G5ZhrgfSN6U3E2YToWakehtF55RA+CNQS0KboTAB2vUH01+Tkmkd8K6UElf41A6cGnnrvs0g==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-modify-sitemaps/-/antora-modify-sitemaps-0.7.0.tgz", + "integrity": "sha512-mx6KhD9rdxq/gMM8NxKOEkbaT8qG8U6VgEx2TPZ0yz4Udo3kYypiJ+hvQtKfcRz1WcU9hHzISV+gV3xvTab39A==", + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + } }, "node_modules/@neo4j-antora/antora-page-roles": { "version": "0.3.2", @@ -337,9 +343,13 @@ "integrity": "sha512-RqmMHcTyM87lJAhjSPdkoUzFxQCjsM2N4+ryVK4GIahAJyNV9OYydBrsjGrDbIh6F4YS+LWG+SXyvLD2zJ1keQ==" }, "node_modules/@neo4j-antora/antora-table-footnotes": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-table-footnotes/-/antora-table-footnotes-0.3.2.tgz", - "integrity": "sha512-DXEGVHMJumoKiY/ZCaGRTXl2OhPziPCHT+arj18TmpU50sUs+hyjOPuTkUXUvBwNZwm109Nm1PJPvKLVIJCZSg==" + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-table-footnotes/-/antora-table-footnotes-0.3.3.tgz", + "integrity": "sha512-0vKB9nMJpfZdKTJc5vOJrNCFTcae61rnw9XyPQX2aibJN4vUTEeKhIS6IaVJWW5M1CflVIU3LoZDpDzeA/GEHw==", + "license": "MIT", + "peerDependencies": { + "@asciidoctor/core": "2.2.8" + } }, "node_modules/@neo4j-antora/mark-terms": { "version": "1.1.0", @@ -494,9 +504,9 @@ } }, "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" }, "node_modules/balanced-match": { "version": "1.0.2", @@ -504,9 +514,9 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", "optional": true }, "node_modules/base64-js": { @@ -760,9 +770,9 @@ } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, "engines": { "node": ">= 0.6" @@ -933,9 +943,9 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "dependencies": { "accepts": "~1.3.8", @@ -943,7 +953,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -957,7 +967,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -972,6 +982,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/fast-copy": { @@ -1291,9 +1305,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "engines": { "node": ">= 4" } @@ -1430,18 +1444,6 @@ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1478,9 +1480,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -1601,9 +1603,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/nodemon": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", - "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -1738,9 +1740,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true }, "node_modules/pend": { @@ -2102,13 +2104,9 @@ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "bin": { "semver": "bin/semver.js" }, @@ -2317,9 +2315,9 @@ } }, "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.0.tgz", + "integrity": "sha512-Qz6MsDZXJ6ur9u+b+4xCG18TluU7PGlRfXVAAjNiGsFrBUt/ioyLkxbFaKJygoPs+/kW4VyBj0bSj89Qu0IGyg==", "dependencies": { "fast-fifo": "^1.3.2", "queue-tick": "^1.0.1", @@ -2369,9 +2367,9 @@ } }, "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.2.tgz", + "integrity": "sha512-/MDslo7ZyWTA2vnk1j7XoDVfXsGk3tp+zFEJHJGm0UjIlQifonVFwlVbQDFh8KJzTBnT8ie115TYqir6bclddA==", "dependencies": { "b4a": "^1.6.4" } @@ -2430,9 +2428,9 @@ } }, "node_modules/uglify-js": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", - "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -2520,12 +2518,6 @@ "node": ">=4" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", @@ -2557,34 +2549,34 @@ }, "dependencies": { "@antora/asciidoc-loader": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.9.tgz", - "integrity": "sha512-flE27T2yI8TX7rUNjbBHWN3iR6s+kBuRBbUPncUFcWjx6mXzll8JLiTkxnc8JXHGzgKlveT+t5AkPYGACLfasg==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.10.tgz", + "integrity": "sha512-np0JkOV37CK7V4eDZUZXf4fQuCKYW3Alxl8FlyzBevXi2Ujv29O82JLbHbv1cyTsvGkGNNB+gzJIx9XBsQ7+Nw==", "requires": { - "@antora/logger": "3.1.9", - "@antora/user-require-helper": "~2.0", + "@antora/logger": "3.1.10", + "@antora/user-require-helper": "~3.0", "@asciidoctor/core": "~2.2" } }, "@antora/cli": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.9.tgz", - "integrity": "sha512-kCUqWX3G/9Pvf8SWZ45ioHwWdOc9uamy2E5/FFwyGiTeu4ubNbadOauLVvMzSZHUxVDnGxXwCsmmQ2HwM919ew==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.10.tgz", + "integrity": "sha512-gp8u9aVM0w1DtWSsB5PwvEfFYKrooPENLhN58RAfdgTrcsTsWw+CDysFZPgEaHB0Y1ZbanR82ZH/f6JVKGcZfQ==", "requires": { - "@antora/logger": "3.1.9", - "@antora/playbook-builder": "3.1.9", - "@antora/user-require-helper": "~2.0", + "@antora/logger": "3.1.10", + "@antora/playbook-builder": "3.1.10", + "@antora/user-require-helper": "~3.0", "commander": "~11.1" } }, "@antora/content-aggregator": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.9.tgz", - "integrity": "sha512-g+UzevPSm5c4R0j1U9uysJfdIUfp++QOHIEBmqjhfx/aIEnOL70zA+WF55Mm+syAfzU3877puI27sOp8qtPglw==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.10.tgz", + "integrity": "sha512-OT6ZcCA7LrtNfrAZUr3hFh+Z/1isKpsfnqFjCDC66NEMqIyzJO99jq0CM66rYlYhyX7mb5BwEua8lHcwpOXNow==", "requires": { - "@antora/expand-path-helper": "~2.0", - "@antora/logger": "3.1.9", - "@antora/user-require-helper": "~2.0", + "@antora/expand-path-helper": "~3.0", + "@antora/logger": "3.1.10", + "@antora/user-require-helper": "~3.0", "braces": "~3.0", "cache-directory": "~2.0", "fast-glob": "~3.3", @@ -2607,73 +2599,73 @@ } }, "@antora/content-classifier": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.9.tgz", - "integrity": "sha512-PVJqwp5uvZE1PlpeJtb0p6al75fN+fmXGIC6DHcKysRnr0xo+sgz8X2r4mnNWdTWRqum2yVigMmmuXYTg3cJlQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.10.tgz", + "integrity": "sha512-3JJl4IIiTX00v/MirK603NoqIcHjGYAaRWt3Q4U03tI1Fv2Aho/ypO3FE45069jFf0Dx2uDJfp5kapb9gaIjdQ==", "requires": { - "@antora/asciidoc-loader": "3.1.9", - "@antora/logger": "3.1.9", + "@antora/asciidoc-loader": "3.1.10", + "@antora/logger": "3.1.10", "mime-types": "~2.1", "vinyl": "~3.0" } }, "@antora/document-converter": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.9.tgz", - "integrity": "sha512-pH7tQaIjcPsFdYkaBEAvA/5ki04IQwQGHoR+2jadKdMl6P+J5KA1VzNnMgyIL6gHn7auJIkoOKadfItRB9lHGQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.10.tgz", + "integrity": "sha512-qi9ctgcKal8tZtWflVo66w+4zCJoBmUKRV+eA9aRRR09KDdU9r514vu1adWNgniPppISr90zD13V5l2JUy/2CQ==", "requires": { - "@antora/asciidoc-loader": "3.1.9" + "@antora/asciidoc-loader": "3.1.10" } }, "@antora/expand-path-helper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@antora/expand-path-helper/-/expand-path-helper-2.0.0.tgz", - "integrity": "sha512-CSMBGC+tI21VS2kGW3PV7T2kQTM5eT3f2GTPVLttwaNYbNxDve08en/huzszHJfxo11CcEs26Ostr0F2c1QqeA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@antora/expand-path-helper/-/expand-path-helper-3.0.0.tgz", + "integrity": "sha512-7PdEIhk97v85/CSm3HynCsX14TR6oIVz1s233nNLsiWubE8tTnpPt4sNRJR+hpmIZ6Bx9c6QDp3XIoiyu/WYYA==" }, "@antora/file-publisher": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.9.tgz", - "integrity": "sha512-C0VwVjuFbE1CVpZDgwYR1gZCNr1tMw5vueyF9wHZH0KCqAsp9iwo7bwj8wKWMPogxcxdYhnAvtDJnYmYFCuDWQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.10.tgz", + "integrity": "sha512-DPR/0d1P+kr3qV4T0Gh81POEO/aCmNWIp/oLUYAhr0HHOcFzgpTUUoLStgcYynZPFRIB7EYKSab+oYSCK17DGA==", "requires": { - "@antora/expand-path-helper": "~2.0", - "@antora/user-require-helper": "~2.0", + "@antora/expand-path-helper": "~3.0", + "@antora/user-require-helper": "~3.0", "vinyl": "~3.0", "yazl": "~2.5" } }, "@antora/logger": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.9.tgz", - "integrity": "sha512-MKuANodcX0lfRyiB+Rxl/Kv7UOxc2glzTYFoIoBB7uzxF0A+AhvUJDmpGQFRFN2ihxy99N3nLJmZpDebwXyE+A==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.10.tgz", + "integrity": "sha512-WSuIxEP2tVrhWtTj/sIrwBDjpi4ldB/1Kpiu4PXmY4/qeWP8thW6u8nXdwdDcWss5zqkZWjourvWKwVq7y8Wjg==", "requires": { - "@antora/expand-path-helper": "~2.0", + "@antora/expand-path-helper": "~3.0", "pino": "~9.2", "pino-pretty": "~11.2", "sonic-boom": "~4.0" } }, "@antora/navigation-builder": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.9.tgz", - "integrity": "sha512-zyl2yNjK31Dl6TRJgnoFb4Czwt9ar3wLTycAdMeZ+U/8YcAUHD8z7NCssPFFvZ0BbUr00NP+gbqDmCr6yz32NQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.10.tgz", + "integrity": "sha512-aLMK49nYsSB3mEZbLkmUXDAUYmscv2AFWu+5c3eqVGkQ6Wgyd79WQ6Bz3/TN9YqkzGL+PqGs0G39F0VQzD23Hw==", "requires": { - "@antora/asciidoc-loader": "3.1.9" + "@antora/asciidoc-loader": "3.1.10" } }, "@antora/page-composer": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.9.tgz", - "integrity": "sha512-X6Qj+J5dfFAGXoCAOaA+R6xRp8UoNMDHsRsB1dUTT2QNzk1Lrq6YkYyljdD2cxkWjLVqQ/pQSP+BJVNFGbqDAQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.10.tgz", + "integrity": "sha512-JoEg8J8HVsnPmAgUrYSGzf0C8rQefXyCi/18ucy0utyfUvlJNsZvUbGUPx62Het9p0JP0FkAz2MTLyDlNdArVg==", "requires": { - "@antora/logger": "3.1.9", + "@antora/logger": "3.1.10", "handlebars": "~4.7", "require-from-string": "~2.0" } }, "@antora/playbook-builder": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.9.tgz", - "integrity": "sha512-MJ/OWz4pReC98nygGTXC5bOL/TDDtCYpSkHFBz2ST4L6tuM8rv9c5+cp//JkwY/QlTOvcuJ0f2xq4a7a5nI7Qw==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.10.tgz", + "integrity": "sha512-UB8UmRYfkKgActTUlotdVS4FKGjaZgTnSXE7Fns1xb3/3HRanWvI+Yze1OmCkGC33cTpoQFnSYp7ySEH8LaiBw==", "requires": { "@iarna/toml": "~2.2", "convict": "~6.2", @@ -2682,65 +2674,65 @@ } }, "@antora/redirect-producer": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.9.tgz", - "integrity": "sha512-9OLwoMhqifsBxTebInh/5W16GdDsdj+YkKG3TiCASlAOYsDbuhbeRPFUlyKKSRkMrtKKnFgHR0Z3DNPXYlH2NQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.10.tgz", + "integrity": "sha512-IbWJGh6LmsxJQ821h0B9JfooofFZBgFLZxsbp/IoTLkBFGLFAY5tDRvB6rvubfNLRoSjM8VjEUXGqVLlwZOb+g==", "requires": { "vinyl": "~3.0" } }, "@antora/site-generator": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.9.tgz", - "integrity": "sha512-YYESPG22tGX1CxRPSAr6acKILCO8JfGkM1OYc7Sw3D7ZvCy1YgZMAaTYK0T5yl9LXg+l/UZi1xq/Ej0qHnYQiw==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.10.tgz", + "integrity": "sha512-NCULYtwUjIyr5FGCymhfG/zDVUmZ6pfmCPorka8mAzo4/GDx1T7bgaRL9rEIyf2AMqcm7apQiAz03mpU4kucsw==", "requires": { - "@antora/asciidoc-loader": "3.1.9", - "@antora/content-aggregator": "3.1.9", - "@antora/content-classifier": "3.1.9", - "@antora/document-converter": "3.1.9", - "@antora/file-publisher": "3.1.9", - "@antora/logger": "3.1.9", - "@antora/navigation-builder": "3.1.9", - "@antora/page-composer": "3.1.9", - "@antora/playbook-builder": "3.1.9", - "@antora/redirect-producer": "3.1.9", - "@antora/site-mapper": "3.1.9", - "@antora/site-publisher": "3.1.9", - "@antora/ui-loader": "3.1.9", - "@antora/user-require-helper": "~2.0" + "@antora/asciidoc-loader": "3.1.10", + "@antora/content-aggregator": "3.1.10", + "@antora/content-classifier": "3.1.10", + "@antora/document-converter": "3.1.10", + "@antora/file-publisher": "3.1.10", + "@antora/logger": "3.1.10", + "@antora/navigation-builder": "3.1.10", + "@antora/page-composer": "3.1.10", + "@antora/playbook-builder": "3.1.10", + "@antora/redirect-producer": "3.1.10", + "@antora/site-mapper": "3.1.10", + "@antora/site-publisher": "3.1.10", + "@antora/ui-loader": "3.1.10", + "@antora/user-require-helper": "~3.0" } }, "@antora/site-generator-default": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.9.tgz", - "integrity": "sha512-m/QCv2o/24VmWZaeqtc6nNEky///GTLLx/pyyihP7uWKvZ0AhGPp6Agv1yaShjKIthBzHJ3JozaMPev2leor+A==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.10.tgz", + "integrity": "sha512-dMhjbklthysj3espwYNkTkADm2Z3EbWThq9gJv/ZuSXGZSXVSwt8b3mBpCTwxOeAKIldnj3fc1pzQxei/7PC2w==", "requires": { - "@antora/site-generator": "3.1.9" + "@antora/site-generator": "3.1.10" } }, "@antora/site-mapper": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.9.tgz", - "integrity": "sha512-9FCObL+JIjBoby8z+beu2uuvAtCjm5EsEQt+16gCIMX1ktVP3W3gVsdRSvVcGcVEpizILFhMawkcQknZPUp5mg==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.10.tgz", + "integrity": "sha512-KY1j/y0uxC2Y7RAo4r4yKv9cgFm8aZoRylZXEODJnwj3tffbZ2ZdRzSWHp6fN0QX/Algrr9JNd9CWrjcj2f3Zw==", "requires": { - "@antora/content-classifier": "3.1.9", + "@antora/content-classifier": "3.1.10", "vinyl": "~3.0" } }, "@antora/site-publisher": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.9.tgz", - "integrity": "sha512-L5To8f4QswZliXu6yB6O7O8CuBbLctjNbxZqP3m0FP7VaOONp85ftzEq1BFEm4BXXSwH1n4ujZx1qGBHP9ooOQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.10.tgz", + "integrity": "sha512-G4xcUWvgth8oeEQwiu9U1cE0miQtYHwKHOobUbDBt2Y6LlC5H31zQQmAyvMwTsGRlvYRgLVtG6j9d6JBwQ6w9Q==", "requires": { - "@antora/file-publisher": "3.1.9" + "@antora/file-publisher": "3.1.10" } }, "@antora/ui-loader": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.9.tgz", - "integrity": "sha512-g0/9dRE5JVMYukIU3x+Rvr41bPdK3sUD2xQIAniRjE6usIZs1mEsTGshVKVEoOqqnSekXE85HVhybjNHsC+qbQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.10.tgz", + "integrity": "sha512-H1f5wI5a5HjLuE/Wexvc8NZy8w83Bhqjka7t1DbwOOqP+LyxFGLx/QbBVKdTtgFNDHVMtNBlplQq0ixeoTSh0A==", "requires": { - "@antora/expand-path-helper": "~2.0", + "@antora/expand-path-helper": "~3.0", "braces": "~3.0", "cache-directory": "~2.0", "fast-glob": "~3.3", @@ -2761,17 +2753,17 @@ } }, "@antora/user-require-helper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@antora/user-require-helper/-/user-require-helper-2.0.0.tgz", - "integrity": "sha512-5fMfBZfw4zLoFdDAPMQX6Frik90uvfD8rXOA4UpXPOUikkX4uT1Rk6m0/4oi8oS3fcjiIl0k/7Nc+eTxW5TcQQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@antora/user-require-helper/-/user-require-helper-3.0.0.tgz", + "integrity": "sha512-KIXb8WYhnrnwH7Jj21l1w+et9k5GvcgcqvLOwxqWLEd0uVZOiMFdqFjqbVm3M+zcrs1JXWMeh2LLvxBbQs3q/Q==", "requires": { - "@antora/expand-path-helper": "~2.0" + "@antora/expand-path-helper": "~3.0" } }, "@asciidoctor/core": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.6.tgz", - "integrity": "sha512-TmB2K5UfpDpSbCNBBntXzKHcAk2EA3/P68jmWvmJvglVUdkO9V6kTAuXVe12+h6C4GK0ndwuCrHHtEVcL5t6pQ==", + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.8.tgz", + "integrity": "sha512-oozXk7ZO1RAd/KLFLkKOhqTcG4GO3CV44WwOFg2gMcCsqCUTarvMT7xERIoWW2WurKbB0/ce+98r01p8xPOlBw==", "requires": { "asciidoctor-opal-runtime": "0.3.3", "unxhr": "1.0.1" @@ -2783,14 +2775,17 @@ "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, "@neo4j-antora/antora-add-notes": { - "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==" + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-add-notes/-/antora-add-notes-0.3.2.tgz", + "integrity": "sha512-Jsv17dEBELSkqplEIZE9b5I2zjYPvoHi4momLRt1FfBRQnBTWbk4kkf2JQojRJ8mQEVscj2tApfTDOAUtAOSLA==" }, "@neo4j-antora/antora-modify-sitemaps": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-modify-sitemaps/-/antora-modify-sitemaps-0.4.4.tgz", - "integrity": "sha512-IkXoilOJquZPB5G5ZhrgfSN6U3E2YToWakehtF55RA+CNQS0KboTAB2vUH01+Tkmkd8K6UElf41A6cGnnrvs0g==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-modify-sitemaps/-/antora-modify-sitemaps-0.7.0.tgz", + "integrity": "sha512-mx6KhD9rdxq/gMM8NxKOEkbaT8qG8U6VgEx2TPZ0yz4Udo3kYypiJ+hvQtKfcRz1WcU9hHzISV+gV3xvTab39A==", + "requires": { + "semver": "^7.6.3" + } }, "@neo4j-antora/antora-page-roles": { "version": "0.3.2", @@ -2798,9 +2793,10 @@ "integrity": "sha512-RqmMHcTyM87lJAhjSPdkoUzFxQCjsM2N4+ryVK4GIahAJyNV9OYydBrsjGrDbIh6F4YS+LWG+SXyvLD2zJ1keQ==" }, "@neo4j-antora/antora-table-footnotes": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-table-footnotes/-/antora-table-footnotes-0.3.2.tgz", - "integrity": "sha512-DXEGVHMJumoKiY/ZCaGRTXl2OhPziPCHT+arj18TmpU50sUs+hyjOPuTkUXUvBwNZwm109Nm1PJPvKLVIJCZSg==" + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-table-footnotes/-/antora-table-footnotes-0.3.3.tgz", + "integrity": "sha512-0vKB9nMJpfZdKTJc5vOJrNCFTcae61rnw9XyPQX2aibJN4vUTEeKhIS6IaVJWW5M1CflVIU3LoZDpDzeA/GEHw==", + "requires": {} }, "@neo4j-antora/mark-terms": { "version": "1.1.0", @@ -2924,9 +2920,9 @@ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" }, "b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" }, "balanced-match": { "version": "1.0.2", @@ -2934,9 +2930,9 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", "optional": true }, "base64-js": { @@ -3110,9 +3106,9 @@ } }, "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true }, "cookie-signature": { @@ -3234,9 +3230,9 @@ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "requires": { "accepts": "~1.3.8", @@ -3244,7 +3240,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -3258,7 +3254,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -3494,9 +3490,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" }, "ignore-by-default": { "version": "1.0.1", @@ -3599,15 +3595,6 @@ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3632,9 +3619,9 @@ "dev": true }, "micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "requires": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -3714,9 +3701,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "nodemon": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", - "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", "dev": true, "requires": { "chokidar": "^3.5.2", @@ -3808,9 +3795,9 @@ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true }, "pend": { @@ -4070,13 +4057,9 @@ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" }, "send": { "version": "0.19.0", @@ -4220,9 +4203,9 @@ "dev": true }, "streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.0.tgz", + "integrity": "sha512-Qz6MsDZXJ6ur9u+b+4xCG18TluU7PGlRfXVAAjNiGsFrBUt/ioyLkxbFaKJygoPs+/kW4VyBj0bSj89Qu0IGyg==", "requires": { "bare-events": "^2.2.0", "fast-fifo": "^1.3.2", @@ -4261,9 +4244,9 @@ } }, "text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.2.tgz", + "integrity": "sha512-/MDslo7ZyWTA2vnk1j7XoDVfXsGk3tp+zFEJHJGm0UjIlQifonVFwlVbQDFh8KJzTBnT8ie115TYqir6bclddA==", "requires": { "b4a": "^1.6.4" } @@ -4310,9 +4293,9 @@ } }, "uglify-js": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", - "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "optional": true }, "undefsafe": { @@ -4376,12 +4359,6 @@ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==" }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", diff --git a/package.json b/package.json index 6fa63e771..b86fe81ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cypher-manual", - "version": "5.0.0", + "version": "2025", "description": "Neo4j Cypher Manual", "main": "server.js", "scripts": { @@ -19,20 +19,20 @@ "author": "Neo4j", "license": "ISC", "dependencies": { - "@antora/cli": "^3.1.9", - "@antora/site-generator-default": "^3.1.9", - "@neo4j-antora/antora-add-notes": "^0.3.1", - "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", + "@antora/cli": "^3.1.10", + "@antora/site-generator-default": "^3.1.10", + "@neo4j-antora/antora-add-notes": "^0.3.2", + "@neo4j-antora/antora-modify-sitemaps": "^0.7.0", "@neo4j-antora/antora-page-roles": "^0.3.1", - "@neo4j-antora/antora-table-footnotes": "^0.3.2", + "@neo4j-antora/antora-table-footnotes": "^0.3.3", "@neo4j-antora/mark-terms": "1.1.0", "@neo4j-documentation/macros": "^1.0.4", "@neo4j-documentation/remote-include": "^1.0.0", "asciidoctor-kroki": "^0.18.1" }, "devDependencies": { - "express": "^4.21.0", - "nodemon": "^3.1.7" + "express": "^4.21.2", + "nodemon": "^3.1.9" }, "overrides": { "@antora/site-generator-default": { diff --git a/preview.yml b/preview.yml index fa119684c..b70cd4e8a 100644 --- a/preview.yml +++ b/preview.yml @@ -22,13 +22,6 @@ ui: urls: html_extension_style: indexify -antora: - extensions: - - require: "@neo4j-antora/antora-modify-sitemaps" - sitemap_version: '5' - sitemap_loc_version: 'current' - move_sitemaps_to_components: true - asciidoc: extensions: - "@neo4j-documentation/remote-include" @@ -53,7 +46,7 @@ asciidoc: includePDF: false nonhtmloutput: "" experimental: '' - copyright: "2024 Neo4j, Inc." + copyright: "2025 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 49ad0451a..b5183d48d 100644 --- a/publish.yml +++ b/publish.yml @@ -22,13 +22,6 @@ ui: urls: html_extension_style: indexify -antora: - extensions: - - require: "@neo4j-antora/antora-modify-sitemaps" - sitemap_version: '5' - sitemap_loc_version: 'current' - move_sitemaps_to_components: true - asciidoc: extensions: - "@neo4j-documentation/remote-include" @@ -45,7 +38,7 @@ asciidoc: page-search-site: Reference Docs page-canonical-root: /docs page-pagination: true - page-no-canonical: true + page-no-canonical: true page-origin-private: false page-hide-toc: false page-mixpanel: 4bfb2414ab973c741b6f067bf06d5575 @@ -53,18 +46,10 @@ asciidoc: includePDF: false nonhtmloutput: "" experimental: '' - copyright: "2024 Neo4j, Inc." + copyright: "2025 Neo4j, Inc." common-license-page-uri: https://neo4j.com/docs/license/ docs-base-uri: https://neo4j.com/docs check-mark: icon:check[] cross-mark: icon:times[] neo4j-base-uri: '' neo4j-docs-base-uri: /docs - # NODES 2024 AD - page-ad-overline-link: https://neo4j.registration.goldcast.io/events/03805ea9-fe3a-4cac-8c15-aa622666531a?utm_source=neodocs&utm_medium=banner&utm_campaign=std - page-ad-image: https://neo4j.com/docs/assets/img/nodes-24.png - page-ad-title: Neo4j Online Developer Conference - page-ad-description: Join us on November 7 for live and unique tech talks over 24 hours across all timezones. - page-ad-link: https://neo4j.registration.goldcast.io/events/03805ea9-fe3a-4cac-8c15-aa622666531a?utm_source=neodocs&utm_medium=banner&utm_campaign=std - page-ad-underline-role: button - page-ad-underline: Register \ No newline at end of file