Overview
Linear Query Statement
In GQL, a linear query statement refers to a query that is executed sequentially, where each statement is processed one after another without any branching or conditional logic. The result is returned in a straightforward, linear progression.
For example, this is a linear query where the MATCH
, FILTER
and RETURN
statements processed in a linear order:
MATCH (:User {_id: "U01"})-[:Follows]->(u:User)
FILTER u.city = "New York"
RETURN u
<linear query statement> ::=
[ <simple linear query statement> ] <result statement>
<simple linear query statement> ::= <simple query statement>...
<simple query statement> ::=
<match statement>
| <filter statement>
| <let statement>
| <for statement>
| <order by statement>
| <limit statement>
| <skip statement>
| <call statement>
<result statement> ::=
<return statement> [ <order by statement> ] [ <skip statement> ] [ <limit statement> ]
Details
- A linear query statement ends with a result statement and may include a linear composition of simple query statements before that.
Composite Query Statement
A composite query statement combines a list of linear query statements with query conjunctions.
<composite query statement> ::=
<linear query statement> <query conjunction> <linear query statement>
[ { <query conjunction> <linear query statement> }... ]
<query conjunction> ::= <set operator> | "OTHERWISE"
<set operator> ::= "UNION" [ <set quantifier> ]
| "EXCEPT" [ <set quantifier> ]
| "INTERSECT" [ <set quantifier> ]
<set quantifier> ::= "DISTINCT" | "ALL"
Details
- The number, order and the names of the columns must be identical in the
RETURN
statements of all linear queries. - The binding tables produced by the linear query statements are composited together.
- If set operator is specified while set quantifier is not, then
DISTINCT
is implicit. The deduplication is performed to the records of the binding table. - You may use different query conjunctions in a composite query statement.
Example Graph
The following examples run against this graph:
To create this graph, run the following query against an empty graph:
INSERT (rowlock:User {_id:'U01', name:'rowlock'}),
(brainy:User {_id:'U02', name:'Brainy'}),
(purplechalk:User {_id:'U03', name:'purplechalk'}),
(mochaeach:User {_id:'U04', name:'mochaeach'}),
(lionbower:User {_id:'U05', name:'lionbower'}),
(c01:Club {_id:'C01'}),
(c02:Club {_id:'C02'}),
(rowlock)-[:Follows]->(brainy),
(brainy)-[:Follows]->(rowlock),
(mochaeach)-[:Follows]->(brainy),
(brainy)-[:Follows]->(purplechalk),
(purplechalk)-[:Follows]->(brainy),
(brainy)-[:Joins]->(c01),
(lionbower)-[:Joins]->(c01),
(mochaeach)-[:Joins]->(c02)
UNION
UNION
combines the results of two or more linear queries into a single result set that includes the distinct rows present in any of the query.
MATCH (n:Club) RETURN n
UNION
MATCH (n) RETURN n
Result: n
_id | _uuid | schema | values |
---|---|---|---|
C02 | Sys-gen | Club | |
C01 | Sys-gen | Club | |
U05 | Sys-gen | User | {name: "lionbower"} |
U04 | Sys-gen | User | {name: "mochaeach"} |
U03 | Sys-gen | User | {name: "purplechalk"} |
U02 | Sys-gen | User | {name: "Brainy"} |
U01 | Sys-gen | User | {name: "rowlock"} |
UNION ALL
UNION ALL
combines the results of two or more linear queries into a single result set that includes all rows from each query without removing duplicates.
MATCH (n:Club) RETURN n
UNION ALL
MATCH (n) RETURN n
Result: n
_id | _uuid | schema | values |
---|---|---|---|
C02 | Sys-gen | Club | |
C01 | Sys-gen | Club | |
U05 | Sys-gen | User | {name: "lionbower"} |
U04 | Sys-gen | User | {name: "mochaeach"} |
U03 | Sys-gen | User | {name: "purplechalk"} |
U02 | Sys-gen | User | {name: "Brainy"} |
U01 | Sys-gen | User | {name: "rowlock"} |
C02 | Sys-gen | Club | |
C01 | Sys-gen | Club |
EXCEPT
EXCEPT
returns a result set that includes distinct rows from the first query that are not present in any of the subsequent queries.
MATCH ({_id: "U02"})-(n) RETURN n
EXCEPT
MATCH ({_id: "U05"})-(n) RETURN n
Result: n
_id | _uuid | schema | values |
---|---|---|---|
U04 | Sys-gen | User | {name: "mochaeach"} |
U03 | Sys-gen | User | {name: "purplechalk"} |
U01 | Sys-gen | User | {name: "rowlock"} |
EXCEPT ALL
EXCEPT ALL
returns a result set that includes rows from the first query that are not present in any of the subsequent queries without removing duplicates.
MATCH ({_id: "U02"})-(n) RETURN n
EXCEPT ALL
MATCH ({_id: "U05"})-(n) RETURN n
Result: n
_id | _uuid | schema | values |
---|---|---|---|
U01 | Sys-gen | User | {name: "rowlock"} |
U03 | Sys-gen | User | {name: "purplechalk"} |
U04 | Sys-gen | User | {name: "mochaeach"} |
U03 | Sys-gen | User | {name: "purplechalk"} |
U01 | Sys-gen | User | {name: "rowlock"} |
INTERSECT
INTERSECT
returns a result set that includes only the distinct rows common to all linear queries, filtering out any rows that do not appear in every query.
MATCH ({_id: "U01"})-(u:User) RETURN u
INTERSECT
MATCH ({_id: "U03"})-(u:User) RETURN u
Result: u
_id | _uuid | schema | values |
---|---|---|---|
U02 | Sys-gen | User | {name: "Brainy"} |
INTERSECT ALL
INTERSECT ALL
returns a result set that includes only the rows common to all linear queries without removing duplicates, filtering out any rows that do not appear in every query.
MATCH ({_id: "U01"})-(u:User) RETURN u
INTERSECT ALL
MATCH ({_id: "U03"})-(u:User) RETURN u
Result: u
_id | _uuid | schema | values |
---|---|---|---|
U02 | Sys-gen | User | {name: "Brainy"} |
U02 | Sys-gen | User | {name: "Brainy"} |
OTHERWISE
OTHERWISE
returns the result set of the first query if it produces results while. If the first query yields no results, it returns the result set of the second query, and so on, until a query with non-empty result set is found.
MATCH ({_id: "U04"})<-[]-(u:User) RETURN u
OTHERWISE
MATCH ({_id: "U02"})<-[]-(u:User) RETURN u
Result: u
_id | _uuid | schema | values |
---|---|---|---|
U01 | Sys-gen | User | {name: "rowlock"} |
U03 | Sys-gen | User | {name: "purplechalk"} |
U04 | Sys-gen | User | {name: "mochaeach"} |
This result set of the first linear query contains a null
value, it is not empty due to the usage of OPTIONAL
:
OTHERWISE
returns the result set of the first linear query if it contains any rows, including a row with the null
value:
OPTIONAL MATCH ({_id: "U04"})<-[]-(u:User) RETURN u
OTHERWISE
MATCH ({_id: "U02"})<-[]-(u:User) RETURN u
Result:
u |
---|
null |
Renaming Columns
You may use the AS
keyword to rename columns to ensure that the binding tables of linear queries can be composited.
MATCH ({_id: "C01"})<-(u) RETURN u.name, 1 AS Club
UNION
MATCH ({_id: "C02"})<-(u) RETURN u.name, 2 AS Club
Result:
u.name | Club |
---|---|
Brainy | 1 |
lionbower | 1 |
mochaeach | 2 |
Using Different Query Conjunctions
MATCH (n:Club) RETURN n._id
OTHERWISE
MATCH (n) RETURN n._id
UNION ALL
MATCH (n)-[]->(:Club) RETURN n._id
Result:
n._id |
---|
C02 |
C02 |
U05 |
U04 |
U02 |