19.3 Supported PGQL Features and Limitations on the Graph Server (PGX)
Learn about the supported and unsupported PGQL functionalities in the graph server (PGX).
Table 19-2 Supported PGQL Functionalities and Limitations on the Graph Server (PGX)
| Features | PGQL on the Graph Server (PGX) | 
|---|---|
| CREATE PROPERTY GRAPH | Supported Limitations:
                                     
 | 
| DROP PROPERTY GRAPH | Not Supported | 
| Fixed-length pattern matching | Supported | 
| Variable-length pattern matching goals | Supported: 
 | 
| Variable-length pattern matching quantifiers | Supported: 
 Limitations: 
 | 
| Variable-length path unnesting | Supported: 
 | 
| OPTIONAL MATCH | Supported | 
| GROUP BY | Supported | 
| HAVING | Supported | 
| Aggregations | Supported: 
 Not Supported 
 | 
| DISTINCT
 | Supported | 
| SELECT v.* | Supported | 
| ORDER BY (+ASC/DESC), LIMIT,
                                OFFSET | Supported | 
| Data Types | Supported: 
 | 
| JSON | No built-in JSON support. However, JSON values can
                                be stored as STRINGand manipulated or queried
                                through user-defined functions (UDFs) written in Java or
                                JavaScript. | 
| Operators | Supported: 
 | 
| Functions and predicates | Supported: 
 | 
| User-defined functions | Supported: 
 | 
| Subqueries: 
 | Supported | 
| GRAPH_TABLEoperator | Supported | 
| INSERT/UPDATE/DELETE | Supported | 
| INTERVALliterals and
                                operations | Supported literals: 
 Supported operations: 
 | 
Also, the following explains certain supported and unsupported PGQL features:
- Support for Selecting All Properties
- Unnesting of Variable-Length Path Queries
- Using INTERVAL Literals in PGQL Queries
- Using Path Modes with PGQL
- Support for PGQL Lateral Subqueries
- Support for PGQL GRAPH_TABLE Operator
- OPTIONAL MATCH Clause
 TheOPTIONAL MATCHclause has the same set of capabilities as a regularMATCHclause, and can contain one or more path patterns followed by an optionalWHEREclause.
- Limitations on Quantifiers
- Limitations on WHERE and COST Clauses in Quantified Patterns
Parent topic: Executing PGQL Queries Against the Graph Server (PGX)
19.3.1 Support for Selecting All Properties
You can use SELECT v.* to select all properties of the vertices or
            edges that bind to the variable v. For example:
                  
SELECT label(n), n.* FROM MATCH (n) ORDER BY "number", "name"On execution, the query output is as shown:
+-----------------------------+
| label(n) | number | name    |
+-----------------------------+
| Account  | 1001   | <null>  |
| Account  | 2090   | <null>  |
| Account  | 8021   | <null>  |
| Account  | 10039  | <null>  |
| Person   | <null> | Camille |
| Person   | <null> | Liam    |
| Person   | <null> | Nikita  |
| Company  | <null> | Oracle  |
+-----------------------------+You can use label expressions to select properties that belong to the specified vertex or edge labels. For example:
SELECT label(n), n.* FROM MATCH (n:Person) ORDER BY "name"The preceding query retrieves all the properties for the specified
                Person label:
                  
+--------------------+
| label(n) | name    |
+--------------------+
| Person   | Camille |
| Person   | Liam    |
| Person   | Nikita  |
+--------------------+You can also specify a PREFIX to avoid duplicate column names in cases
            where you select all properties using multiple variables. For example:
                  
SELECT n.* PREFIX 'n_', e.* PREFIX 'e_', m.* PREFIX 'm_'
FROM MATCH (n:Account) -[e:transaction]-> (m:Account)
ORDER BY "e_amount"The query output is as shown:
+--------------------------------+
| n_number | e_amount | m_number |
+--------------------------------+
| 10039    | 1000.0   | 8021     |
| 8021     | 1500.3   | 1001     |
| 8021     | 3000.7   | 1001     |
| 2090     | 9900.0   | 10039    |
| 1001     | 9999.5   | 2090     |
+--------------------------------+19.3.2 Unnesting of Variable-Length Path Queries
Unnesting of variable-length path queries (such as, SHORTEST or
                CHEAPEST paths) to obtain a separate row for each vertex or edge
            along a path is supported.
                  
- ONE ROW PER MATCH(default option)
- ONE ROW PER VERTEX(vertex_variable)
- ONE ROW PER STEP(edge_source_variable,edge_variable,edge_destination_variable)
For example, the following PGQL query uses the ONE ROW PER
                STEP option:
                  
SELECT  v1.ACCT_ID AS src_no, k.TXN_AMOUNT, v2.ACCT_ID AS dest_no 
FROM MATCH ALL SHORTEST (a:Accounts) -[e:transfers]->+ (b:Accounts)
ONE ROW PER STEP( v1,k,v2 )
WHERE a.ACCT_ID = 284 AND b.ACCT_ID = 616On execution, the preceding query retrieves one row for every edge on the path that is bound by the corresponding source and destination vertices:
+-------------------------------+
| src_no | TXN_AMOUNT | dest_no |
+-------------------------------+
| 744    | 1000.0     | 616     |
| 772    | 1000.0     | 744     |
| 284    | 1000.0     | 772     |
| 744    | 1000.0     | 616     |
| 772    | 1500.0     | 744     |
| 284    | 1000.0     | 772     |
+-------------------------------+You can also use the Graph Visualization tool to visualize edges using ONE ROW
                PER STEP along a path:
                  
Figure 19-1 Visualizing Unnesting of Variable-Length Path Queries

Description of "Figure 19-1 Visualizing Unnesting of Variable-Length Path Queries"
Also, when using the ONE ROW PER STEP option, if the path
            is empty (that is, the path length is zero), then it has a single step such that the
            first vertex variable (v1) is bound but the edge variable
                (e) and the second vertex variable (v2) are
            unbound. Note that accessing the properties of unbound variables will result in NULL
            values.
                  
An example for a query with the ONE ROW PER VERTEX option
            is as follows:
                  
SELECT k.acct_id AS id, k.acct_name AS name
FROM MATCH ANY SHORTEST (a:Accounts) ((src:Accounts)-[e:transfers]->){1,3}(b:Accounts)
ONE ROW PER VERTEX(k)
WHERE a.acct_id=284 AND b.acct_id=616On execution, the preceding query retrieves one row per vertex along a path:
+----------------+
| id  | name     |
+----------------+
| 616 | Account4 |
| 744 | Account3 |
| 772 | Account2 |
| 284 | Account1 |
+---------------+
Built-in Function Support for Recursive Path Unnesting Queries
PGQL supports the following two built-in functions, which can be used in
                combination with any of the path unnesting option (ONE ROW PER
                    VERTEX, ONE ROW PER STEP or ONE ROW PER
                    MATCH):
                     
- MATCH_NUMBER(k): Returns a unique per-path identifier for each unnested path (that is, if two rows come from the same path, they have the same- MATCH_NUMBER(k)).
- ELEMENT_NUMBER(k): Returns the element number of a vertex or an edge along a path. Vertices are numbered with odd numbers, the leftmost vertex is numbered- 1, the second- 3, then- 5and so on. Edges are assigned with even numbers, starting with- 2for the leftmost edge,- 4for the next one, and so on.
For example, the following PGQL query uses the MATCH_NUMBER(k) and
                    ELEMENT_NUMBER(k) functions with ONE ROW PER
                    VERTEX option:
                     
SELECT k.*, match_number(k), element_number(k)
FROM MATCH ANY SHORTEST (a:Accounts) -[e:transfers]->* (b:Accounts) ONE ROW PER VERTEX ( k )
WHERE a.acct_id = 284 AND b.acct_id = 616The preceding query produces the following output on execution. Note that the
                    element_number(k) returned for the vertices are odd numbered
                values. Since the preceding query uses ANY path pattern, there is
                only one arbitrary path displayed in the output. Therefore
                    match_number(k) is the same for all the rows in the path.
                     
+-----------------------------------------------------------+
| ACCT_ID | ACCT_NAME | match_number(k) | element_number(k) |
+-----------------------------------------------------------+
| 616     | Account   | 0               | 7                 |
| 744     | Account   | 0               | 5                 |
| 772     | Account   | 0               | 3                 |
| 284     | Account   | 0               | 1                 |
+-----------------------------------------------------------+
The following example shows a PGQL query using
                    MATCH_NUMBER(k) and ELEMENT_NUMBER(k)
                functions with ONE ROW PER STEP option: 
                     
SELECT v1.acct_id AS src_no,k.txn_amount,v2.acct_id AS dest_no, match_number(k), element_number(k)
FROM MATCH ALL SHORTEST (a:Accounts) -[e:transfers]->+ (b:Accounts)
ONE ROW PER STEP( v1,k,v2 )
WHERE a.acct_id = 284 AND b.acct_id = 616The preceding query output is as shown. Note that there are two paths
                identified by match_number(k)and the edges are displayed with even
                numbered element_number(k) values.
                     
+---------------------------------------------------------------------+
| src_no | txn_amount | dest_no | match_number(k) | element_number(k) |
+---------------------------------------------------------------------+
| 744    | 1000.0     | 616     | 0               | 6                 |
| 772    | 1000.0     | 744     | 0               | 4                 |
| 284    | 1000.0     | 772     | 0               | 2                 |
| 744    | 1000.0     | 616     | 1               | 6                 |
| 772    | 1500.0     | 744     | 1               | 4                 |
| 284    | 1000.0     | 772     | 1               | 2                 |
+---------------------------------------------------------------------+19.3.3 Using INTERVAL Literals in PGQL Queries
You can use INTERVAL literals in PGQL
                queries to add or subtract intervals to or from PGQL temporal data types
                respectively.
                     
An INTERVAL type is a
                period of time, which consists of the keyword "INTERVAL" followed
                by a numeral and a temporal unit. For example, INTERVAL '1'
                DAY.
                     
The following table shows the valid temporal units
                that are supported in INTERVAL values:
                     
Table 19-3 Valid values for fields in
                        INTERVAL values
                        
| Keyword | Supported Valid Values | 
|---|---|
| YEAR | Unconstrained except by <interval leading field precision> | 
| MONTH | Months (within years) (0-11) | 
| DAY | Unconstrained except by <interval leading field precision> | 
| HOUR | Hours (within days) (0-23) | 
| MINUTE | Minutes (within hours) (0-59) | 
| SECOND | Seconds (within minutes) (0-59.999...) | 
The following INTERVAL operations are supported
                on a temporal data type:
                     
- TEMPORAL TYPE + INTERVAL
- INTERVAL + TEMPORAL TYPE
- TEMPORAL TYPE - INTERVAL
For example, the following PGQL query retrieves persons where
                    n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01
                    00:00:00':
                     
opg4j> graph.queryPgql("SELECT n.name, n.birthdate FROM MATCH (n:Person) WHERE n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01 00:00:00'").print()graph.queryPgql("SELECT n.name, n.birthdate FROM MATCH (n:Person) WHERE n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01 00:00:00'").print();graph.query_pgql("SELECT n.name, n.birthdate FROM MATCH (n:Person) WHERE n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01 00:00:00'").print()On execution, the query output is as shown:
+--------------------------+
| name  | birthdate        |
+--------------------------+
| Mary  | 1982-09-25T00:00 |
| Alice | 1987-02-01T00:00 |
+--------------------------+19.3.4 Using Path Modes with PGQL
The following path modes are available in combination with ANY,
                ALL, ANY SHORTEST, SHORTEST k,
            and ALL SHORTEST:
                  
- WALK(default path mode): A walk is traversing a graph through a sequence of vertices and edges. The vertices and edges visited in a walk can be repeated. Hence there is no filtering of paths in this default path mode.
- TRAIL: A trail is traversing a graph without repeating the edges. Therefore, path bindings with repeated edges are not returned.
 In the preceding output, both the paths contain the vertices 8021 and 1001 twice but they are still valid trails as long as no edges are repeated.- SELECT CAST(a.number AS STRING) || ' -> ' || LISTAGG(x.number, ' -> ') AS accounts_along_path FROM MATCH ALL TRAIL PATHS (a IS account) (-[IS transaction]-> (x)){2,} (b IS Account) WHERE a.number = 8021 AND b.number = 1001 +-----------------------------------------------+ | accounts_along_path | +-----------------------------------------------+ | 8021 -> 1001 -> 2090 -> 10039 -> 8021 -> 1001 | | 8021 -> 1001 -> 2090 -> 10039 -> 8021 -> 1001 | +-----------------------------------------------+
- ACYCLIC: If the starting and ending vertex in a graph traversal are different, then this implies that there are no cycles in the path. In this case, the path bindings with repeated vertices are not returned.
 The preceding query requested 10 shortest paths. But only two are returned since all the other paths are cyclic.- SELECT CAST(a.number AS STRING) || ' -> ' || LISTAGG(x.number, ' -> ') AS accounts_along_path FROM MATCH SHORTEST 10 ACYCLIC PATHS (a IS account) (-[IS transaction]-> (x))+ (b) WHERE a.number = 10039 AND b.number = 1001 +-----------------------+ | accounts_along_path | +-----------------------+ | 10039 -> 8021 -> 1001 | | 10039 -> 8021 -> 1001 | +-----------------------+
- SIMPLE: A simple walk is traversing a graph without repeating the vertices. Therefore, path bindings with repeated vertices are not returned. The only exception is when the repeated vertex is the first and the last in a path.
 The preceding query returns a cyclic path. This path is a valid simple path since it starts and ends in the same vertex and there is no other cycle in the path.- SELECT CAST(a.number AS STRING) || ' -> ' || LISTAGG(x.number, ' -> ') AS accounts_along_path FROM MATCH ANY SIMPLE PATH (a IS account) (-[IS transaction]-> (x))+ (a) WHERE a.number = 10039 +----------------------------------------+ | accounts_along_path | +----------------------------------------+ | 10039 -> 8021 -> 1001 -> 2090 -> 10039 | +----------------------------------------+
Note that the path modes are syntactically placed after ANY,
                ALL, ANY SHORTEST, SHORTEST k,
                ALL SHORTEST, CHEAPEST, and CHEAPEST
                k. The path mode is optionally followed by a PATH or
                PATHS keyword.
                  
Note that using TRAIL, ACYCLIC, or
                SIMPLE matching path modes for all unbounded quantifiers guarantees
            that the result set of a graph pattern matching will be finite.
                  
19.3.5 Support for PGQL Lateral Subqueries
You can use a LATERAL subquery to pass the output rows of one query
            into another.
                  
For example, you can use the ORDER BY or GROUP
                                BY clause on top of another ORDER BY or
                                GROUP BY clause:
                  
/* Find the top-5 largest transactions and return the account number
   that received the highest number of such large transactions */
SELECT recipient, COUNT(*) AS num_large_transactions
FROM LATERAL ( SELECT m.number AS recipient
               FROM MATCH (n:account) -[e:transaction]-> (m:account)
               ORDER BY e.amount DESC
               LIMIT 5 )
GROUP BY recipient
ORDER BY num_large_transactions DESC
LIMIT 1Also, the LATERAL subquery in the FROM
            clause can be followed by one or more MATCH clauses. For example:
                  
SELECT path_num, elem_num, owner.name
FROM LATERAL ( SELECT v, MATCHNUM(v) AS path_num, ELEMENT_NUMBER(v) AS elem_num
               FROM MATCH SHORTEST 2 PATHS (a1:account) -[e:transaction]->* (a2:account)
                      ONE ROW PER VERTEX ( v )
               WHERE a1.number = 10039 AND a2.number = 2090 )
    , MATCH (v) -[:owner]-> (owner:Person|Company)
ORDER BY path_num, elem_num Note that the FROM clause may contain any number of MATCH
            clauses and LATERAL subqueries.
                  
Both, MATCH clause followed by a
                LATERAL subquery, or a LATERAL subquery followed
            by one or more LATERAL subqueries are supported.
                  
Also, note the following:
- Variables exported from previous table expressions can be used in subsequent table expressions.
- Operators like ORDER BYandLIMITcan be used followed by additional pattern matching.
- Variables that are not projected from a LATERALsubquery cannot be accessed in the outer query.
The following example query first retrieves a list of companies, Then, it finds the respective accounts of the companies. Finally, it finds the top 2 transactions made to the account of each company in the initial list.
SELECT c.name, a.number, t.amount FROM 
  LATERAL ( SELECT c FROM MATCH (c:Company) ORDER BY c.name LIMIT 1),
  MATCH (a:Account)->(c), 
  LATERAL (SELECT t FROM MATCH ()-[t:Transaction]->(a) ORDER BY t.amount DESC LIMIT 2)In the following query, the LATERAL subquery is followed by two other
                LATERAL subqueries. Each subquery builds upon the output from the
            previous clauses:
                  
SELECT f.number as fundsAccount FROM
LATERAL ( SELECT p FROM MATCH (p:Person) WHERE p.name = 'Nikita'),
LATERAL ( SELECT a FROM MATCH (a)->(p)),
LATERAL ( SELECT f FROM MATCH (f)->(a))19.3.6 Support for PGQL GRAPH_TABLE Operator
 The GRAPH_TABLE operator in PGQL increases
                                the interoperability between graphs loaded into the graph server
                                (PGX) and the graphs on the database. 
                     
- The label predicate in the graph pattern
                                                  MATCHquery must use theISkeyword.
- To limit the number of output rows, use the
                                                  FETCH [FIRST/NEXT] x [ROW/ROWS]clause instead of theLIMIT xclause.
- To verify the orientation of the edge, use
                                                  v IS [NOT] SOURCE [OF] e/v IS [NOT] DESTINATION [OF] eas the standard form instead of[NOT] is_source_of(e, v)/[NOT] is_destination_of(e, v).
- To verify if the vertex or edge has the
                                                given label, use the x IS [NOT] LABELED <label_string>predicate as an alternative forhas_label(x, <label_string>).
- To match the kshortest paths, useMATCH (n) –[e]->* (m) KEEP SHORTEST kas the standard form ofMATCH TOP k SHORTEST (n) –[e]->* (m).
- ALLkeyword optional in front of fixed-length path patterns.- MATCH (n) –[e]->{1,4} (m)as an alternative for- MATCH ALL (n) –[e]->{1,4} (m).
- MATCH <path pattern> KEEP <path pattern prefix> <WHERE clause>as an alternative for- MATCH <path pattern prefix> <path pattern> <WHERE clause>
The following shows a few query examples using the
                                                GRAPH_TABLE operator:
                     
Example 19-2 Aggregation Query Using TRAIL path mode with
                                        ALL
SELECT *
FROM GRAPH_TABLE ( financial_transactions
       MATCH ALL TRAIL (a IS account) -[e IS transaction]->* (b IS account)
       /* optional ONE ROW PER VERTEX/STEP clause here */
       WHERE a.number = 8021 AND b.number = 1001
       COLUMNS ( LISTAGG(e.amount, ', ') AS amounts )
     )ORDER BY amountsThe preceding query produces the following output:
+----------------------------------------+
| amounts                                |
+----------------------------------------+
| 1500.3                                 |
| 1500.3, 9999.5, 9900.0, 1000.0, 3000.7 |
| 3000.7                                 |
| 3000.7, 9999.5, 9900.0, 1000.0, 1500.3 |
+----------------------------------------+Example 19-3 Aggregation Query Using KEEP Clause
                     
SELECT *
FROM GRAPH_TABLE ( financial_transactions
       MATCH (a IS Account) -[e IS transaction]->+ (a)
       KEEP SIMPLE PATHS
       WHERE a.number = 10039
       COLUMNS ( LISTAGG(e.amount, ', ') AS amounts_along_path,
                 SUM(e.amount) AS total_amount )
     )
ORDER BY total_amount DESCThe preceding query produces the following output:
+-----------------------------------------------+
| amounts_along_path             | total_amount |
+-----------------------------------------------+
| 1000.0, 3000.7, 9999.5, 9900.0 | 23900.2      |
| 1000.0, 1500.3, 9999.5, 9900.0 | 22399.8      |
+-----------------------------------------------+19.3.7 OPTIONAL MATCH Clause
The OPTIONAL MATCH clause has the same set of capabilities
        as a regular MATCH clause, and can contain one or more path patterns
        followed by an optional WHERE clause.
                  
Using OPTIONAL MATCH in a PGQL query is similar to a
                LEFT OUTER JOIN in a SQL query.
                  
The OPTIONAL keyword can be applied to a MATCH clause
            in a PGQL query as shown:
                  
SELECT * FROM MATCH (person), OPTIONAL MATCH ((person)->(address) WHERE address.number=33)The resulting query output lists all persons with and without
                address.number=33:
                  
+-------------------------+
| person  |    address    |
+-------------------------+
| Nikita  | Greenwhich 33 |
| Camille | Silver St 33  |
| Liam    |    <null>     |
| John    |    <null>     |
+-------------------------+The following shows an example of an OPTIONAL MATCH clause containing
            multiple path patterns and a WHERE clause.
                  
-- List all people as well as their addresses in zip code 12345
SELECT p.name, s.street_name, z.zip_code
FROM MATCH (p IS person),
     OPTIONAL MATCH ( (p) -[e1:has_address]-> (a:address),
                      (a) -[e2:street]-> (s:street),
                      (a) -[e3:zip_code]-> (z:zip_code)
                      WHERE z.zip_code = 12345 )In the preceding query, note that the WHERE clause is present inside the
            parenthesis. This indicates that the filter is applied before the
                OPTIONAL binding is executed. The query output may appear as
            shown:
                  
John P.   1st Street    12345
Mary M.   NULL          NULL      <== NULL value means Mary M. has no address with zip 12345
Jane X.   NULL          NULL      <== NULL value means Jane X. has no address with zip 12345If the WHERE clause is placed outside of the OPTIONAL
                MATCH clause, then the filter will be applied after the
                OPTIONAL clause, essentially removing all unbound variables added
            by the OPTIONAL clause.
                  
-- List all people as well as their addresses, then filter out addresses that do not have zip code 12345
SELECT p.name, s.street_name, z.zip_code
FROM MATCH (p IS person),
     OPTIONAL MATCH ( (p) -[e1:has_address]-> (a:address),
                      (a) -[e2:has_street]-> (s:street),
                      (a) -[e3:has_zip_code]-> (z:zip_code)
                    )
WHERE z.zip_code = 12345
John P.   1st Street    12345OPTIONAL MATCH can be used in conjunction with
                LATERAL subqueries, scalar subqueries, ORDER BY or
                GROUP BY clauses. However, OPTIONAL MATCH is not
            supported with the GRAPH_TABLE operator.
                  
19.3.8 Limitations on Quantifiers
Although all quantifiers such as *, +, and
                {1,4} are supported for reachability and shortest path patterns,
            the only quantifier that is supported for cheapest path patterns is *
            (zero or more).
                  
19.3.9 Limitations on WHERE and COST Clauses in Quantified Patterns
The WHERE and COST clauses in quantified patterns,
      such as reachability patterns or shortest and cheapest path patterns, are limited to
      referencing a single variable only.
                  
The following are examples of queries that are not supported because the
        WHERE or COST clauses reference two variables
        e and x instead of zero or
        one: 
                  
... PATH p AS (n) –[e]-> (m) WHERE e.prop > m.prop ...
... SHORTEST ( (n) (-[e]-> (x) WHERE e.prop + x.prop > 10)* (m) ) ...
... CHEAPEST ( (n) (-[e]-> (x) COST e.prop + x.prop )* (m) ) ...The following query is supported because the subquery only references a single
      variable a from the outer scope, while the variable c does
      not count since it is newly introduced in the subquery:
                  
... PATH p AS (a) -> (b)
      WHERE EXISTS ( SELECT * FROM MATCH (a) -> (c) ) ...