
Graph querying spent its first decade as a collection of vendor dialects. That changed in April 2024, when ISO published GQL as the international standard for property graph databases, written by the working group that maintains SQL.
The standard draws on graph languages that had already converged on similar answers, so its pattern syntax looks familiar to anyone who has written Cypher. This post covers what GQL defines, how a query is structured and evaluated, its core concepts and features, its syntax and common operations, how it sits alongside Cypher, Gremlin, and SQL, what adopting it buys a team, and whether it still implies adopting a graph database.
GQL is a declarative database language for the labeled property graph, specified as ISO/IEC 39075:2024. The standard’s scope covers “data structures and basic operations on property graphs,” including “creating, accessing, querying, maintaining, and controlling property graphs and the data they comprise.”
That scope is wider than the word query suggests. GQL is a complete database language, covering schema declaration, data modification, catalog management, and session and transaction control alongside the pattern-matching surface most people mean by graph query language. The comparison point is SQL, which likewise spans DDL, DML, and transaction control in one specification.
ISO defines the language but ships no reference engine. Implementations support different parts of the standard, so an engine’s documented feature surface matters more than a broad claim of GQL support.
One naming collision is worth clearing early. GQL is not GraphQL. GraphQL is an API query language for fetching application-defined object graphs, usually over HTTP though its specification is transport-agnostic, with no notion of nodes, edges, or traversal in the database sense. The names are close; nothing else about them is.
The idea of a standalone graph query language to complement SQL was raised by members of ISO SC 32/WG 3 in early 2017, the same working group responsible for SQL. ISO approved the GQL project in September 2019, and published the standard in April 2024.
WG3 did not design GQL from scratch. It drew on work already established across the graph ecosystem, including Cypher, PGQL, G-CORE, and GSQL, alongside SQL itself. This lineage is why GQL’s pattern syntax and many of its concepts look familiar across existing graph languages.
A second standard advanced in parallel. SQL/PGQ, published as part of SQL:2023, brings graph pattern matching into SQL over graph views of relational tables. GQL is the standalone graph language. The two were developed through the same WG3 process and share a common pattern-matching foundation.
A GQL query is a pipeline of statements. Each statement receives a table of variable bindings, the working table, transforms it, and passes the result on. Execution begins from a unit table of one row and no columns, and the final RETURN produces the result table. Because reading order matches data flow order, a long query reads top to bottom without unwinding subqueries.
MATCH (c:Customer)-[:OWNS]->(a:Account) -- unit table in, (c, a) table out
LET label = c.firstName || ' ' || c.lastName -- adds a derived column
FILTER a.status = 'FROZEN' -- drops rows
ORDER BY label -- sorts rows
LIMIT 10 -- truncates rows
RETURN label, a.accountNumber AS account -- projects the result tableThis is linear composition, the organizing idea of the language. The sequential reading is a semantic guarantee about results, not a mandate on execution: an engine may reorder statements or evaluate them in parallel as long as the answer is unchanged. Whole queries compose the same way, with NEXT feeding one query’s result table into the next as its working table and UNION, EXCEPT, and INTERSECT combining them.
Nodes and edges. A GQL graph is a labeled property graph. Nodes represent entities, and edges represent directed relationships between them. Both may carry labels and name-value properties.
Graph types. A graph type is the schema: which node and edge types may exist, what properties they carry, which endpoints an edge may connect, and what constraints apply. An open graph type places no constraints on what the graph may contain, while a closed graph type admits only what it declares.
Catalog and sessions. Graphs and graph types are named catalog objects addressed by paths, and a statement selects its working graph with USE. This lets one language address several graphs rather than assume one per connection.
Binding variables. Variables carry data between statements, and reusing a variable name inside a pattern is how GQL expresses a join. In (c:Company)<-[:WORKS_AT]-(x:Person)-[:KNOWS]-(y:Person)-[:WORKS_AT]->(c), the second mention of c constrains both people to the same employer. A pattern without a quantifier binds singleton variables holding one element; a quantified pattern binds group variables holding the list matched along the path, which is why aggregating over a path needs no collect step.
Between them these fix what a query can refer to. The catalog says which graph, the graph type says what may exist inside it, and the binding variables say what the query has matched so far. Everything the language adds on top is a way of describing shapes over those three.
Declarative pattern matching. A pattern is a drawing of the shape you want: () for a node, -[]-> for a directed edge, labels after a colon, and property predicates either inline or in a WHERE clause attached to the element. Label expressions let one pattern cover several cases, combining labels with | for disjunction, & for conjunction, and ! for negation, so (:Person&!Employee) and (:City|Country) are single-element patterns rather than separate queries.
Quantified paths and path modes. A quantifier on an edge expresses reachability: -[:KNOWS]->{1,3} matches chains of one to three hops. Because unbounded traversal over a cyclic graph can enumerate forever, GQL makes the repetition rule explicit through path modes. WALK allows repeated nodes and edges, TRAIL forbids repeating an edge, SIMPLE forbids repeating a node except at the endpoints, and ACYCLIC forbids any repeated node. Path search prefixes such as ALL, ANY, ANY SHORTEST, and ALL SHORTEST then pick which matching paths per endpoint pair to keep.
Composability. Linear composition, NEXT, and the composite set operators assemble a complex query from readable pieces instead of nested subqueries, so a five-line query and a fifty-line query have the same shape.
Schema declared in the language. Graph types declare node and edge types, their property types, and the endpoint pairs an edge may connect; the first version stops short of key and uniqueness constraints. The declarations use the same pattern-like notation as the queries they govern.
The through-line is explicitness. Path semantics, schema strictness, and transaction behavior are part of the language rather than assumptions left to each implementation.
A GQL query is a sequence of statements, optionally preceded by a graph selector. A linear query in that form ends in RETURN, or in FINISH when it is declared to produce none. Statements are the executable units; clauses such as WHERE, GROUP BY, and YIELD modify them.
The statement vocabulary is small. MATCH and OPTIONAL MATCH bind pattern variables, LET adds computed columns, FILTER removes rows, ORDER BY sorts, OFFSET and LIMIT page, and RETURN projects and aggregates. Aggregation lives in RETURN with an optional GROUP BY, so there is no separate aggregation statement.
Patterns compose the way the graph does. A path is written left to right, several patterns can be listed comma-separated in one MATCH to express a branching structure, and a path mode prefixes the pattern it governs:
USE finance
MATCH TRAIL (src:Account)-[t:TRANSFER]->{1,4}(dst:Account)
WHERE src.accountNumber = 'ACC-1001' AND dst.riskFlag = TRUE
RETURN
dst.accountNumber AS destination,
size(t) AS hops,
sum(t.amount) AS routed_total
ORDER BY routed_total DESC
LIMIT 20Here t is a group variable bound to the list of TRANSFER edges along each matched path, which is what makes size(t) and sum(t.amount) meaningful without an intermediate step. TRAIL guarantees no transfer edge is counted twice, so a cycle between two accounts cannot inflate the total.
The surface stays small because the same pieces recombine. A short list of statements covers the query language, clauses modify those statements, and the pattern carries its own quantifier and path mode, so a harder traversal means a more specific pattern, not new syntax.
Reading accounts for most day-to-day work, and three shapes cover nearly all of it.
Read and filter. The common shape is MATCH, then FILTER, then RETURN. Filtering also attaches to a pattern element directly, as in MATCH (a:Account WHERE a.balance > 10000), which lets the engine apply the predicate while matching rather than afterward.
Aggregate. RETURN count(*) AS total, or with grouping, RETURN c.country, count(a) AS accounts GROUP BY c.country.
Traverse a variable number of hops. A quantified edge with an explicit path mode expresses reachability directly, as in the transfer example above.
Writing and schema management run the same pattern syntax in the other direction.
Insert. INSERT writes a whole pattern at once, nodes and edges together:
INSERT (c:Customer {id: 'C-88', name: 'Avery Stare'})
-[:OWNS {since: DATE '2026-07-15'}]->
(a:Account {accountNumber: 'ACC-2044', status: 'ACTIVE'})Update and delete. SET assigns properties or adds labels, REMOVE drops either, and DELETE removes elements. DETACH DELETE removes a node and its connected edges in one statement.
MATCH (a:Account {accountNumber: 'ACC-2044'})
SET a.status = 'FROZEN'
REMOVE a.promoCodeClosing that account removes it and the OWNS edge inserted above together:
MATCH (a:Account {accountNumber: 'ACC-2044'})
DETACH DELETE aDefine schema and manage graphs. CREATE GRAPH TYPE declares node types, edge types, and their property types, and CREATE GRAPH ... TYPED instantiates a graph against one:
CREATE GRAPH TYPE financeType AS {
(customerType: Customer {id :: STRING NOT NULL, name :: STRING}),
(accountType: Account {accountNumber :: STRING NOT NULL, status :: STRING, promoCode :: STRING}),
(customerType)-[: OWNS {since :: DATE}]->(accountType)
}
CREATE GRAPH finance TYPED financeTypeDROP GRAPH removes the graph. Together, these statements let the same language define, instantiate, query, and remove graph structures.
These are three different kinds of comparison. The first two are other property graph languages; the third is a different data model that met the graph halfway.
GQL and Cypher share a pattern-matching lineage. Cypher began at Neo4j, later opened as openCypher, and became a major influence on GQL’s syntax. GQL adds a standardized language around that surface, including graph types and catalog operations.
GQL and Gremlin are the genuine paradigm difference. Gremlin is Apache TinkerPop’s traversal language, where a query is a chain of steps that usually reads as a plan for walking the graph. GQL is declarative: you describe the shape and the planner decides the walk. The choice tracks how a team prefers to reason about traversals, a trade-off covered in Gremlin vs Cypher.
GQL and SQL are designed to coexist. They come from one committee, and SQL/PGQ is the bridge: graph pattern syntax embedded in a SQL statement over a graph view of existing tables. GQL is the standalone language for graph workloads rather than an operator inside a relational query.
The table compares languages, not complete engines. Cypher dialects vary by vendor, Gremlin execution depends on the TinkerPop implementation, and GQL and SQL/PGQ engines support different subsets of their standards. Verify the target engine’s feature surface before treating language-level compatibility as product-level compatibility.
Language risk drops. An ISO standard gives engines a shared target and a common vocabulary for describing compatibility. Portability is not automatic, but differences can be assessed against a stable baseline rather than unrelated vendor languages.
Skills and tooling transfer. A shared language gives training material, drivers, query builders, linters, and visualization tools a common surface to target.
Semantics move into the query text. Path modes, path search prefixes, and graph types make traversal and schema choices explicit. A query that says TRAIL and ALL SHORTEST documents the path behavior it expects.
One language covers the graph’s lifecycle. Schema, loading, querying, and transactions live in one specification, so the same syntax that defines a graph type also queries the graph it types.
The caveat is implementation coverage. Engines support different subsets of GQL and may combine standard features with their own extensions. Read a specific engine’s feature list rather than treating GQL support as a binary label.
Choosing a graph language and choosing where the graph lives have often been coupled. A dedicated graph database usually needs a pipeline that keeps it synchronized with the systems of record. Nothing in GQL requires that arrangement. The standard specifies a language and a data model, not a storage engine.
For teams using Cypher-family syntax today, PuppyGraph runs openCypher and Gremlin queries directly over existing tables in SQL databases, data warehouses, and data lakes and lakehouses, including direct reads of open table formats like Iceberg and Delta Lake. A user-defined schema maps those tables to nodes and edges, and a query compiles into a plan of node and edge operators that runs in its own distributed engine, which is what lets it optimize specifically for multi-hop traversals. Support for GQL itself is on the roadmap; openCypher and Gremlin are the shipped languages today.
GQL is the ISO standard for property graph databases: a full database language covering pattern matching, path semantics, schema, catalog, and transactions. Its organizing ideas are linear composition, where each statement transforms a working table, and explicitness, where traversal semantics and schema strictness are written in the query. Implementation coverage still varies by engine.
Try the forever-free PuppyGraph Developer Edition and book a demo with the team to see how openCypher and Gremlin queries run over warehouse and lakehouse tables, with no graph-specific ETL, so graph patterns and path traversals reach the data where it already lives.
Get started with PuppyGraph!
Developer Edition
Enterprise Edition