diff --git a/modules/ROOT/images/graph_match_clause.svg b/modules/ROOT/images/graph_match_clause.svg index 9426d8918..d2481798f 100644 --- a/modules/ROOT/images/graph_match_clause.svg +++ b/modules/ROOT/images/graph_match_clause.svg @@ -1 +1 @@ -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 +DIRECTEDACTED_INrole:'Gordon Gekko'ACTED_INrole:'Carl Fox'ACTED_INrole:'President Andrew Shepherd'ACTED_INrole:'A.J. MacInerney'ACTED_INrole:'Bud Fox'DIRECTEDPersonname:'Oliver Stone'Movietitle:'Wall Street'Personname:'Michael Douglas'Personname:'Martin Sheen'Movietitle:'The American President'Personname:'Charlie Sheen'Personname:'Rob Reiner' diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index a26f46c2b..f04bf857d 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -1,80 +1,56 @@ :description: The `MATCH` clause is used to search for the pattern described in it. include::https://raw.githubusercontent.com/neo4j-graphacademy/courses/main/asciidoc/ads/data-analysis.adoc[] -[[query-match]] = MATCH -[[match-introduction]] -== Introduction - -The `MATCH` clause allows you to specify the patterns Neo4j will search for in the database. -This is the primary way of getting data into the current set of bindings. -For more information about how `MATCH` is used to find patterns (including xref:patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path patterns], xref:patterns/variable-length-patterns.adoc#quantified-relationships[quantified relationships], and xref:patterns/shortest-paths.adoc[shortest paths]), see the section on xref::patterns/index.adoc[Patterns]. - -`MATCH` is often coupled to a `WHERE` part which adds restrictions, or predicates, to the `MATCH` patterns, making them more specific. -The predicates are part of the pattern description, and should not be considered a filter applied only after the matching is done. -_This means that `WHERE` should always be put together with the `MATCH` clause it belongs to._ - -`MATCH` can occur at the beginning of the query or later, possibly after a `WITH`. -If it is the first clause, nothing will have been bound yet, and Neo4j will design a search to find the results matching the clause and any associated predicates specified in any `WHERE` part. -This could involve a scan of the database, a search for nodes having a certain label, or a search of an index to find starting points for the pattern matching. -Nodes and relationships found by this search are available as _bound pattern elements,_ and can be used for pattern matching of paths. -They can also be used in any further `MATCH` clauses, where Neo4j will use the known elements, and from there find further unknown elements. - -Cypher is declarative, and so usually the query itself does not specify the algorithm to use to perform the search. -Neo4j will automatically work out the best approach to finding start nodes and matching patterns. -Predicates in `WHERE` parts can be evaluated before pattern matching, during pattern matching, or after finding matches. -However, there are cases where you can influence the decisions taken by the query compiler. -Read more about indexes in xref:indexes/search-performance-indexes/managing-indexes.adoc[], and more about specifying hints to force Neo4j to solve a query in a specific way in xref::indexes/search-performance-indexes/index-hints.adoc[Planner hints and the USING keyword]. - +The `MATCH` clause enables you to define specific patterns that the database will search for within its graph structure. +The `MATCH` clause can specify the nodes, relationships, and properties in a pattern, allowing for queries that traverse the graph to retrieve relevant data. [[match-example-graph]] == Example graph The following graph is used for the examples below: -image::graph_match_clause.svg[width="600",role="middle"] +image::graph_match_clause.svg[width="500",role="middle"] To recreate the graph, run the following query against an empty Neo4j database: [source, cypher, role=test-setup] ---- -CREATE - (charlie:Person {name: 'Charlie Sheen'}), - (martin:Person {name: 'Martin Sheen'}), - (michael:Person {name: 'Michael Douglas'}), - (oliver:Person {name: 'Oliver Stone'}), - (rob:Person {name: 'Rob Reiner'}), - (wallStreet:Movie {title: 'Wall Street'}), - (charlie)-[:ACTED_IN {role: 'Bud Fox'}]->(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident), - (martin)-[:FATHER_OF]->(charlie) +CREATE (charlie:Person {name: 'Charlie Sheen'}), + (martin:Person {name: 'Martin Sheen'}), + (michael:Person {name: 'Michael Douglas'}), + (oliver:Person {name: 'Oliver Stone'}), + (rob:Person {name: 'Rob Reiner'}), + (wallStreet:Movie {title: 'Wall Street'}), + (charlie)-[:ACTED_IN {role: 'Bud Fox'}]->(wallStreet), + (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), + (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), + (oliver)-[:DIRECTED]->(wallStreet), + (thePresident:Movie {title: 'The American President'}), + (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), + (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), + (rob)-[:DIRECTED]->(thePresident) ---- +[[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 +[source, cypher] ---- MATCH (n) RETURN n ---- -Returns all the nodes in the database. - .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 +[source, cypher] ---- -MATCH (:Person {name: 'Oliver Stone'})--(movie:Movie) -RETURN movie.title +MATCH (n:!Movie) +RETURN labels(n) AS label, count(n) AS labelCount ---- -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]. + +[[find-relationships]] +== Find relationships -[[label-expression-match-or-expression]] -=== Match with a label expression for the node 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]. -A match with an `OR` expression for the node label returns the nodes that contains both the specified labels. +[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]. -.Query +[[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"}) -[[relationship-basics]] -== Relationship basics +1+d| Rows: 1 +|=== -[[outgoing-relationships]] -=== Outgoing relationships +[[directed-relationship-patterns]] +=== Directed relationship patterns -When the direction of a relationship is of interest, it is shown by using `-->` or `<--`. -For example: +The direction of a relationship in a pattern is indicated by arrows: `-->` or `<--`. -.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 ---- -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 +---- -Variables and specific relationship types can be included in the same pattern. -For example: +.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 ---- -Returns the `ACTED_IN` roles for the movie `Wall Street`. +[NOTE] +The above query uses an xref:subqueries/existential.adoc[`EXISTS` subquery]. .Result [role="queryresult",options="header,footer",cols="1* Parameters]. -Databases occasionally contain relationship types including non-alphanumerical characters, or with spaces in them. -These are created using backticks (```). +[[find-paths]] +== Find paths -For example, the following query creates a relationship which contains a space (`OLD FRIENDS`) between `Martin Sheen` and `Rob Reiner`. +The `MATCH` clause can also be used to bind whole paths to variables. -.Query -[source, cypher, indent=0] +.Find all paths matching a pattern +[source, cypher] ---- -MATCH - (martin:Person {name: 'Martin Sheen'}), - (rob:Person {name: 'Rob Reiner'}) -CREATE (rob)-[:`OLD FRIENDS`]->(martin) +MATCH path = ()-[:ACTED_IN]->(movie:Movie) +RETURN path ---- -This leads to the following graph: +.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 +|=== -image::graph_match_clause_backtick.svg[width="600", role="middle"] -.Query -[source, cypher, indent=0] +.Find paths matching a pattern including a `WHERE` predicate +[source, cypher] ---- -MATCH (n {name: 'Rob Reiner'})-[r:`OLD FRIENDS`]->() -RETURN type(r) +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-rels]] -=== Multiple relationships +== Multiple MATCH clauses, the WITH clause, and clause composition -Relationships can be expressed by using multiple statements in the form of `()--()`, or they can be strung together. -For example: +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. -.Query -[source, cypher, indent=0] +.Chaining consecutive `MATCH` clauses +[source, cypher] ---- -MATCH (charlie {name: 'Charlie Sheen'})-[:ACTED_IN]->(movie)<-[:DIRECTED]-(director) -RETURN movie.title, director.name +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 ---- - -Returns the movie in which `Charlie Sheen` acted and its director. +<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] +The above query uses the xref:functions/aggregating.adoc#functions-collect[`collect()` function]. + +.Result +[role="queryresult",options="header,footer",cols="3*