Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article gives Graph Query Language examples focusing on core query patterns, and shows common real world use cases for GQL using realistic graph schemas and queries.
The following examples show the GQL syntax supported, from simple to complex patterns.
Note
Before you try these examples, set up your environment to use GQL. See Getting Started for details. Ensure you set the client request properties to use GQL, and set the graph reference function to your graph data source.
GQL support is in preview. Features and syntax can change based on feedback and ongoing development.
Core GQL query patterns
WHERE: Use standard comparison and logical operators similar to KQL
where
clauses.WHERE
clauses filter patterns based on node and edge properties. They work like KQL or SQL WHERE clauses but operate on graph patterns.RETURN): Use
RETURN
statements to project results from matched patterns. They specify what data to output from your graph query.
For a full list of supported GQL functions and operators, see Graph Query Language (GQL) reference.
Basic pattern matching examples
Basic pattern matching without variables
The simplest pattern matches any relationship without referencing the matched values.
MATCH ()-[]-()
RETURN COUNT(*)
This query finds all relationships in the graph. The empty parentheses ()
represent anonymous nodes, and []
represents anonymous edges. Because we don't assign variables, we can only count the matches but can't access their properties.
Pattern matching with variables
To access the matched nodes and edges, assign them to variables.
MATCH (n)-[e]->(n2)
RETURN COUNT(*)
n
represents the source node, e
represents the directed edge, and n2
represents the target node. You reference these variables to access properties, but in this example you're still just counting matches.
Access node properties
Once you have variables, you access properties of matched nodes.
MATCH (person)-[e]->(target)
RETURN person.name, target.name, e.lbl
ORDER BY person.name
LIMIT 2
Output
This query returns the names of connected entities and the type of relationship between them, ordered by person.name and limited to two results to manage output size.
Although those entities are named person
and target
, there's no restriction that ensures they're actually a person.
person.name | target.name | e.lbl |
---|---|---|
Alice | TechCorp | works_at |
Alice | Seattle | located_at |
Filter by node labels
Use labels to match specific types of nodes. Specify labels with a colon after the variable name.
MATCH (person:Person)
RETURN person.name
ORDER BY person.name
LIMIT 5
Output
This query matches only nodes with the "Person" label and returns their names, limited to five results to avoid large result sets.
person.name |
---|
Alice |
Bob |
Carol |
David |
Emma |
Filter by edge labels
MATCH (person:Person)-[works:works_at]->(company:Company)
RETURN person.name, company.name
Filter by edge labels without variables
For this example, switch back to the original G_Doc_Transient() graph, which has the "knows" relationship:
#crp query_graph_reference=G_Doc_Transient()
Filter by edge labels without assigning the edge to a variable when you don't need to access its properties.
MATCH (p1:Person)-[:knows]->(p2:Person)
RETURN p1.name, p2.name
ORDER BY p1.name
Output
This query finds Person nodes connected to other Person nodes through "knows" relationships. The edge is filtered by its label but isn't assigned to a variable because you only need the connected nodes.
p1.name | p2.name |
---|---|
Alice | Bob |
Bob | Carol |
Carol | David |
David | Emma |
Filter by properties with WHERE
Use WHERE clauses to filter based on property values.
MATCH (person:Person)
WHERE person.properties.age > 25
RETURN person.name, person.properties.age
This query finds people over 25 years old and returns their names and ages. The WHERE clause filters the matched nodes by the age property.
Inline property filters
Filter by properties directly in the pattern using inline conditions.
MATCH (person:Person {name: 'Bob'})
RETURN person.properties.age
This query finds the person named "Bob" and returns their age. The {name: 'Bob'}
syntax filters nodes where the name property equals 'Bob'.
Multiple inline conditions
Specify multiple property conditions inline.
MATCH (person:Person {name: 'Bob'})
WHERE person.properties.age = 30
RETURN person as Bob
This query finds the person with both the specified name and age. It returns the person node as "Bob."
Variable length paths
Use variable length path patterns with quantifiers for multi-hop relationships:
MATCH (s)-[]->{1,3}(e)
RETURN s.name, e.name
This query finds paths with 1 to 3 hops. The {1,3}
quantifier sets the minimum and maximum path length.
Unbounded variable length paths
Specify open ranges for paths of variable length:
MATCH (center)-[]->{1,}(connected)
WHERE center.name = 'Alice'
RETURN DISTINCT connected.name
This query finds all nodes you can reach from Alice through paths of one or more hops. The {1,}
quantifier means "one or more hops."
Path functions
Use path functions to extract information about the matched paths, including the nodes and relationships that make up the path:
MATCH fof = (person)-[:knows]->{2,2}()
RETURN fof, NODES(fof) AS NodesOfPath, RELATIONSHIPS(fof) AS EdgesOfPath
LIMIT 1
This query finds paths exactly 2 hops long using the "knows" relationship. The path variable fof
contains the entire path as an array of alternating nodes and edges. The NODES()
function extracts just the nodes from the path, and RELATIONSHIPS()
extracts just the edges.
Output
fof | NodesOfPath | EdgesOfPath |
---|---|---|
[{"id": "p3", "lbl": "Person", "name": "Carol", "properties": {"age": 28}, "$labels": ["Person"]}, {"source": "p3", "target": "p4", "lbl": "knows", "since": 2022, "$labels": ["knows"]}, {"id": "p4", "lbl": "Person", "name": "David", "properties": {"age": 35}, "$labels": ["Person"]}, {"source": "p4", "target": "p5", "lbl": "knows", "since": 2023, "$labels": ["knows"]}, {"id": "p5", "lbl": "Person", "name": "Emma", "properties": {"age": 26}, "$labels": ["Person"]}] |
[{"id": "p3", "lbl": "Person", "name": "Carol", "properties": {"age": 28}, "$labels": ["Person"]}, {"id": "p4", "lbl": "Person", "name": "David", "properties": {"age": 35}, "$labels": ["Person"]}, {"id": "p5", "lbl": "Person", "name": "Emma", "properties": {"age": 26}, "$labels": ["Person"]}] |
[{"source": "p3", "target": "p4", "lbl": "knows", "since": 2022, "$labels": ["knows"]}, {"source": "p4", "target": "p5", "lbl": "knows", "since": 2023, "$labels": ["knows"]}] |
Basic property filtering
Filter nodes based on a single property condition:
MATCH (person:Person)
WHERE person.properties.age > 26
RETURN person.name, person.properties.age
ORDER BY person.name
Output
This query finds all Person
nodes where the age
property is greater than 26.
person.name | person.properties.age |
---|---|
Bob | 30 |
Carol | 28 |
David | 35 |
Range filtering with AND
To create a range, combine multiple conditions:
MATCH (person:Person)
WHERE person.properties.age >= 28 AND person.properties.age <= 35
RETURN person.name, person.properties.age
This query shows people whose ages are between 28 and 35, inclusive.
Edge property filtering
Filter by edge properties to find specific types of relationships.
MATCH (person:Person)-[wa:works_at]->(c:Company)
WHERE wa.since >= 2022
RETURN person.name, c.name, wa.since
This query shows people who start working at companies in 2022 or later. It filters by the since
property of the works_at
relationship.
String pattern matching
Use string functions to match text patterns.
MATCH (person:Person)
WHERE person.name STARTS WITH 'Al'
RETURN COUNT(*)
This query counts people whose names start with 'Al'. It provides a quick summary without returning a large result set.
String contains matching
Check if a string has a specific substring.
MATCH (person:Person)
WHERE person.name CONTAINS 'i'
RETURN person.name
This query finds people whose names have 'i' anywhere in the string. This match is case sensitive.
Inequality comparisons
Use comparison operators to exclude specific values:
MATCH (person:Person)-[wa:works_at]->(company:Company)
WHERE person.properties.age > 25 AND company.name <> 'TechCorp'
RETURN person.name, company.name
This query shows people over 25 who work at companies other than 'TechCorp'.
Null value checking
Check for the presence or absence of property values.
MATCH (person:Person)
WHERE person.properties.age IS NOT NULL
RETURN person.name, person.properties.age
This query finds all people who have an age recorded (non-null age property).
Logical OR operations
Use OR to match multiple conditions
MATCH (person:Person)
WHERE person.properties.age > 30 OR person.name CONTAINS 'a'
RETURN person.name, person.properties.age
This query finds people who are over 30 years old or have 'a' in their name.
Return specific properties
Return individual properties from matched nodes.
MATCH (person:Person)
RETURN person.name, person.properties.age
This query returns the name and age properties for each Person node. Each row in the result shows these two values.
Return with aliases
Use aliases to rename output columns for clarity.
MATCH (person:Person)-[employment:works_at]->(company:Company)
RETURN person.name AS Employee, company.name AS Company, employment.since AS WorkingSince
This query returns employee names, company names, and employment start dates with descriptive column headers.
Return property bags (JSON objects)
Create custom JSON objects by combining multiple properties in the RETURN clause.
MATCH (person:Person)
RETURN {age: person.properties.age, name: person.name} AS myPropertyBag
LIMIT 1
Output
This query creates a custom JSON object (property bag) containing selected properties from matched Person nodes. The property bag combines the person's age and name into a single structured object.
myPropertyBag |
---|
{ "age": 28, "name": "Carol" } |
Return entire nodes and edges
MATCH (person:Person)-[e]->(company:Company)
WHERE person.name = 'Alice'
RETURN person, e, company
Output
This query returns the complete node and edge objects for Alice's relationship with companies, including all properties.
person | e | company |
---|---|---|
{"id":"p1","lbl":"Person","name":"Alice","properties":{"age": 25}} | {"source":"p1","target":"c1","lbl":"works_at","since":2020} | {"id":"c1","lbl":"Company","name":"TechCorp","properties":{}} |
Count matching patterns
Use COUNT(*) to count the number of pattern matches.
MATCH (person:Person)-[:likes]->(p2:Person)
RETURN person.name, COUNT(*) AS LikesGiven
This query counts how many people each person likes and groups the results by person name.
Aggregate with MIN and MAX
Find minimum and maximum values across all matches.
MATCH (person:Person)
RETURN MIN(cast(person.properties.age as int)) AS Youngest, MAX(cast(person.properties.age as int)) AS Oldest
This query finds the youngest and oldest ages among all people in the graph.
Collect values into arrays
Use COLLECT_LIST to gather multiple values into arrays.
MATCH (person:Person)
RETURN COLLECT_LIST(person.name) AS AllNames
This query collects all person names into a single array.
Return distinct values
Remove duplicates from your results.
MATCH (person:Person)-[]->(company:Company)
RETURN DISTINCT company.name
This query returns each unique company name only once, even if multiple people are connected to the same company.
Return ordered results
Sort your results using ORDER BY.
MATCH (person:Person)
RETURN person.name, person.properties.age
ORDER BY cast(person.properties.age as int) DESC
This query returns people sorted by age in descending order, oldest first.
Limit result count
Restrict the number of results returned.
MATCH (person:Person)
WHERE person.properties.age > 25
RETURN person.name
ORDER BY cast(person.properties.age as int)
LIMIT 5
This query returns only the first five people over 25 years old, ordered by age.
Comparable KQL: Similar to KQL project
, summarize
, sort
, and take
operators.
Advanced patterns examples
Advanced patterns let you match complex graph structures and label combinations.
Label unions (OR)
Match nodes that have any of the specified labels:
MATCH (entity:Person | Company)
RETURN entity
This query matches nodes that have either the "Person" or "Company" label. The pipe symbol |
means logical OR for labels.
Label intersections (AND)
Match nodes that have all of the specified labels:
MATCH (person:Person & Male)
RETURN person.name
This query matches nodes that have both the "Person" and "Male" labels. The ampersand &
means logical AND for labels.
Label negation (NOT)
Match nodes that do NOT have the specified label:
MATCH (person:!Female)
RETURN person.name
This query matches all nodes that don't have the "Female" label. The exclamation mark !
means logical NOT for labels.
Variable length paths with exact range
Match paths with a specific number of hops:
MATCH (s)-[es]->{2,2}(e)
RETURN s, es, e
This query finds paths that are exactly two hops long. The {2,2}
quantifier sets both minimum and maximum path length to 2.
Variable length paths with open range
Match paths with a minimum number of hops but no maximum:
MATCH (s)-[p]->{1,}(e)
RETURN s, e, p
This query finds paths that are one or more hops long. The {1,}
quantifier means one or more hops with no upper limit.
Zero-length paths
Match nodes with themselves (identity relationships):
MATCH (n)-[]->{0,0}(same_n)
RETURN n
This query matches each node with itself through a zero-length path. The {0,0}
quantifier sets the path length to zero, so each node is paired with itself.
Named path variables
Assign entire paths to variables for later use:
MATCH p = (person:Person)-[:works_at]->(company:Company)
RETURN p
This query assigns the entire pattern to the variable p
, which you can return or use in other parts of the query. The path variable has the complete sequence of nodes and edges.
Optional matching
Use OPTIONAL MATCH
to find patterns that might not exist, returning empty values when patterns don't match:
MATCH (p:Person)
WHERE p.properties.age < 30
OPTIONAL MATCH (p)->(c:City)
WHERE c.name <> 'Seattle'
RETURN p.name as Person, c.name as City
This query finds all Person
nodes where age is less than 30, and optionally matches cities they're connected to (excluding Seattle). If a person isn't connected to any city (or only to Seattle), the City column returns empty, but the person is still included in the results.
Output
The optional match ensures that people without city connections are still returned:
Person | City |
---|---|
Carol | Portland |
Emma | Portland |
Alice |
Multi-hop named paths
Create named paths that span multiple relationships:
MATCH full_path = (s)-[first_edge]->(middle)-[second_edge]->(e)
RETURN full_path, s.name, e.name
This query creates a named path variable full_path
that captures a two-hop pattern and returns specific properties from the s and e nodes.
Comparable KQL: Uses advanced graph-match
operator features for complex pattern matching.
Complex multi-pattern examples
Cross product filtering with FILTER
When using multiple separate MATCH
patterns, GQL creates a cross product of all matching combinations. The FILTER
keyword allows you to filter this cross product based on conditions involving variables from different patterns.
This example shows how to filter the cross product of persons and cities:
MATCH (p:Person)
MATCH (c:City)
FILTER p.name = 'Carol' or c.name = 'Seattle'
RETURN p.name as Person, c.name as City
LIMIT 2
Output
Person | City |
---|---|
Carol | Portland |
David | Seattle |
This query demonstrates how the FILTER
keyword operates on the cross product of all Person nodes and all City nodes, returning only the combinations where either the person's name is 'Carol' or the city's name is 'Seattle'.
Cross-town likes with company filter
This example combines multiple patterns and filters in one statement:
- Find a pair of people where p1 likes p2.
- Link each person to their home city. p1's city name must start with "San", and p2 must live in a different city.
- Make sure p2 works at TechCorp.
- Only include pairs where the two people have different ages.
The first RETURN
shows every qualifying match—both people, their cities, and the company—so you can review the raw results.
The second RETURN
aggregates all matches to output a single value: the average age of all "liked" people (p2) who meet these criteria.
MATCH (p1:Person)-[:likes]->(p2:Person),
(p1)-[:located_at]->(home:City),
(p2)-[:located_at]->(home2:City),
(p2)-[:works_at]->(work:Company {name: 'TechCorp'})
WHERE cast(p1.properties.age as int) <> cast(p2.properties.age as int) and home.name <> home2.name and home.name starts with 'San'
RETURN p1.name, p2.name, home.name, work.name, home2.name
Output
p1.name | p2.name | home.name | work.name | home2.name |
---|---|---|---|---|
David | Alice | San Francisco | TechCorp | Seattle |
MATCH (p1:Person)-[:likes]->(p2:Person),
(p1)-[:located_at]->(home:City),
(p2)-[:located_at]->(home2:City),
(p2)-[:works_at]->(work:Company {name: 'TechCorp'})
WHERE cast(p1.properties.age as int) <> cast(p2.properties.age as int) and home.name <> home2.name and home.name starts with 'San'
RETURN AVG(cast(p2.properties.age as int)) AS AvgAgeLikedAcrossTowns
Output
AvgAgeLikedAcrossTowns |
---|
25 |
This example shows how GQL lets you express complex multi-pattern queries with cross-variable filtering, inline property matching, string pattern matching, and aggregation—all in one readable statement.
Temporal data analysis with DURATION functions
GQL provides comprehensive support for temporal data analysis using duration functions. These functions enable you to perform time-based filtering, calculations, and comparisons on graph data with timestamps.
Supported duration units
The DURATION()
function supports a wide range of time units with flexible, case-insensitive syntax and returns a timespan
object:
Time Unit | Supported Names | Example | Timespan Output |
---|---|---|---|
Days | days , day |
DURATION({days: 7}) |
7.00:00:00 |
Hours | hours , hour , HOURS |
DURATION({hours: 24}) |
1.00:00:00 |
Minutes | minutes , minute , MINUTES |
DURATION({minutes: 30}) |
00:30:00 |
Seconds | seconds , second , SECONDS , secOnd |
DURATION({seconds: 45}) |
00:00:45 |
Milliseconds | milliseconds , millisecond |
DURATION({milliseconds: 500}) |
00:00:00.5000000 |
Microseconds | microseconds , microsecond , micRosecond |
DURATION({microseconds: 1000}) |
00:00:00.0010000 |
Nanoseconds | nanoseconds , nanosecond , nanoSecond |
DURATION({nanoseconds: 1000000}) |
00:00:00.0010000 |
You can combine multiple units in a single duration object: DURATION({days: 1.8, minutes: 8, seconds: 7})
returns 1.00:08:07
.
Complex temporal boundary analysis
Combine duration functions with timestamp arithmetic for precise temporal filtering:
MATCH (system:System)-[event:generated]->(alert:Alert)
WHERE event.event_timestamp <= zoned_datetime("2012-01-01 08:00:00.0") + DURATION({days: 3})
AND event.event_timestamp > zoned_datetime("2012-01-01 08:00:00.0") + DURATION({minutes: 1})
RETURN
system.name,
alert.severity,
event.event_timestamp,
DURATION_BETWEEN(zoned_datetime("2012-01-01 08:00:00.0"), event.event_timestamp) AS time_since_baseline
ORDER BY event.event_timestamp
This query finds alerts generated within a specific time window (between 1 minute and 3 days after a baseline date) and calculates the duration between the baseline and each event.
Output
system.name | alert.severity | event_timestamp | time_since_baseline |
---|---|---|---|
WebServer01 | Warning | 2012-01-01T08:01:30.0Z | 00:01:30 |
Database02 | Critical | 2012-01-02T20:15:45.0Z | 1.12:15:45 |
LoadBalancer | Info | 2012-01-04T16:30:00.0Z | 3.08:30:00 |
Social networks use case: Friend recommendations
Social media platforms use GQL to suggest potential friends based on mutual relationships.
We will be using the LDBC Social Network Benchmark dataset (see GQL Sample Data).
Set up the graph reference to point to the LDBC dataset.
#crp query_graph_reference=graph('LDBC_SNB_Interactive')
Find potential friends through mutual connections.
MATCH (p1:PERSON {firstName: 'Karl', lastName: 'Muller'})-[:KNOWS]-(p2:PERSON)-[:KNOWS]-(p3:PERSON), (p1)-[:IS_LOCATED_IN]-(c1:PLACE), (p3)-[:IS_LOCATED_IN]-(c1) WHERE p1.id <> p3.id RETURN DISTINCT p3.firstName, p3.lastName
Output
This query suggests friends for Karl who have mutual connections and live in the same location.
p3.firstName | p3.lastName |
---|---|
Alfred | Hoffmann |
Hans | Becker |
Wilhelm | Muller |