UltipaDocs
Try Playground
  • Introduction
  • GQL vs Other Languages
    • Overview
    • Node and Edge Patterns
    • Path Patterns
    • Quantified Paths
    • Shortest Paths
    • Graph Patterns
    • Overview
    • Closed Graph
    • Open Graph
    • Graph Sharding and Storage
    • Constraints
    • Unique Identifiers
    • INSERT
    • INSERT OVERWRITE
    • UPSERT
    • SET
    • REMOVE
    • DELETE
    • Query Composition
    • Result Table and Visualization
    • MATCH
    • OPTIONAL MATCH
    • FILTER
    • LET
    • FOR
    • ORDER BY
    • LIMIT
    • SKIP
    • CALL
    • RETURN
    • Composite Query
    • NEXT
    • All Functions
    • Scalar Functions
    • Path Functions
    • Aggregate Functions
    • Mathematical Functions
    • Trigonometric Functions
    • String Functions
    • List Functions
    • Datetime Functions
    • Spatial Functions
    • Type Conversion Functions
    • Table Functions
    • AI & Vector Functions
    • Database Functions
  • Operators
  • Predicates
  • Expressions
    • Index
    • Full-text Index
    • Vector Index
  • Transactions
  • Triggers
    • Process
    • Job
    • Execution Plan
    • Variables
    • Values and Types
    • Comments
    • Reserved Words
    • Syntactic Notation
  • GQL Conformance
  1. Docs
  2. /
  3. ISO GQL
  4. /
  5. Querying

OPTIONAL MATCH

Overview

OPTIONAL MATCH functions similarly to MATCH in that it attempts to find patterns in the graph, but it tolerates the absence of matches:

  • MATCH: If a graph pattern has no matches, it returns no records.
  • OPTIONAL MATCH: If a graph pattern has no matches, it returns a null value.

The OPTIONAL keyword can be applied to a single MATCH statement, or to a block of MATCH statements.

Example Graph

CREATE GRAPH myGraph { 
  NODE User ({name string}),
  NODE Club ({since uint32}),
  EDGE Follows ()-[{createdOn date}]->(),
  EDGE Joins ()-[{memberNo uint32}]->()
}

Checking Existence

The user rowlock hasn't joined the club C01, therefore this query yields no results:

GQL
MATCH (:User {name: "rowlock"})->(c:Club {_id: "C01"})
RETURN c

Result: No return data

If uses OPTIONAL MATCH, the query returns a row with null, indicating the absence of the match:

GQL
OPTIONAL MATCH (:User {name: "rowlock"})->(c:Club {_id: "C01"})
RETURN c

Result:

c
null

Retaining All Incoming Records

In this query, the two MATCH statements are equi-joined on the common variable u:

GQL
MATCH (u:User)
MATCH (u)-[:Joins]->(c:Club)
RETURN u.name, c._id

Result:

u.namec._id
mochaeachC02
BrainyC01
lionbowerC01

If we replace the second MATCH with OPTIONAL MATCH, the result sets of the two statements are left-joined, meaning all records from the first MATCH are preserved:

GQL
MATCH (u:User)
OPTIONAL MATCH (u)-[:Joins]->(c:Club)
RETURN u.name, c._id

Result:

u.namec._id
mochaeachC02
BrainyC01
rowlocknull
lionbowerC01
purplechalknull

Keeping the Query Running

In the case when a statement produces empty results, the query halts at that point, as there is no data for subsequent statements to operate on.

For example, the following query has no return because the MATCH fails to find a matching node. As a result, the RETURN is never executed.

GQL
MATCH (u:User) WHERE u.name = "Masterpiece1989"
RETURN CASE WHEN u IS NULL THEN "User not found" ELSE u END 

Result: No return data

To ensure that the query continues executing even when no match is found, use OPTIONAL MATCH:

GQL
OPTIONAL MATCH (u:User) WHERE u.name = "Masterpiece1989"
RETURN CASE WHEN u IS NULL THEN "User not found" ELSE u END

Result: User not found

The Evaluation of WHERE

When using OPTIONAL MATCH, keep in mind that the WHERE clause is evaluated during pattern matching, i.e., before the OPTIONAL logic is applied, not after.

This query returns users who have no followers:

GQL
MATCH (n:User)
OPTIONAL MATCH p = (n)<-[:Follows]-()
FILTER p IS NULL
RETURN COLLECT_LIST(n.name) AS Names

Result:

Names
["mochaeach","rowlock"]

You won’t get the expected results if replaces FILTER with WHERE, since the WHERE clause is evaluated before OPTIONAL is applied:

GQL
MATCH (n:User)
OPTIONAL MATCH p = (n)<-[:Follows]-()
WHERE p IS NULL
RETURN COLLECT_LIST(n.name) AS Names

Result:

Names
["mochaeach","Brainy","rowlock","lionbower","purplechalk"]

Optional MATCH Block

You can wrap multiple MATCH statements inside braces {} or parentheses () and apply OPTIONAL to the entire block. This means that the whole block is treated as a unit: if any part of it fails to match, it doesn't stop the query—instead, it returns null for all variables introduced inside that block.

GQL
FOR name IN ["rowlock", "Masterpiece1989", "Brainy"]
OPTIONAL {
    MATCH (u:User) WHERE u.name = name
    MATCH (u)->(c:Club)
}
RETURN table(name, u.name, c._id)

Result:

nameu.namec._id
rowlocknullnull
Masterpiece1989nullnull
BrainyBrainyC01