# Graph queries > BigQuery property graphs let you write a JOIN chain as a pattern of nodes and edges in GQL. Querylab.io completes labels and properties from your graph's schema, converts JOIN queries to GRAPH_TABLE and back, and draws node-edge results in a Graph tab. Source: https://docs.querylab.io/writing-sql/graph-queries/ **Query 1 · GQL** ```sql GRAPH FinGraph MATCH (sender:Person)-[t:Transfers]->(receiver:Person) FILTER t.amount > 500 RETURN sender.name AS sender, receiver.name AS receiver, t.amount ``` The query above is the same as this JOIN, and both return the same rows: **the same query as JOINs** ```sql SELECT p1.name AS sender, p2.name AS receiver, t.amount FROM Person AS p1 JOIN Transfers AS t ON p1.id = t.source_id JOIN Person AS p2 ON t.dest_id = p2.id WHERE t.amount > 500 ``` GQL is BigQuery's own syntax: a standalone `GRAPH … MATCH … RETURN` query, `GRAPH_TABLE(…)` as a table source inside SQL, and `CREATE PROPERTY GRAPH` to define a graph. All three get highlighting, completions, hover and checks in the editor. For the language itself, see Google's [GQL reference](https://cloud.google.com/bigquery/docs/reference/standard-sql/graph-intro). ## Defining a graph `CREATE PROPERTY GRAPH` names the node and edge tables, their keys, labels, and which columns are exposed as properties. The editor checks the statement as you type: an edge table without both `SOURCE KEY` and `DESTINATION KEY`, or a graph without `NODE TABLES`, gets an error squiggle. ```sql CREATE PROPERTY GRAPH FinGraph NODE TABLES ( Person KEY (id) LABEL Person PROPERTIES (id, name, age), Account KEY (id) LABEL Account PROPERTIES (id, balance) ) EDGE TABLES ( Transfers KEY (transfer_id) SOURCE KEY (source_id) REFERENCES Person (id) DESTINATION KEY (dest_id) REFERENCES Person (id) LABEL Transfers PROPERTIES (transfer_id, amount, date), Owns KEY (account_id) SOURCE KEY (owner_id) REFERENCES Person (id) DESTINATION KEY (account_id) REFERENCES Account (id) LABEL Owns PROPERTIES (since) ); ``` Graphs you've created show in the schema tree under their dataset. Opening one gives a details tab with the DDL, node and edge tables, labels, and a connectivity summary. Inside `PROPERTIES (…)`, typing `M` offers a `MEASURE(…)` snippet per allowed aggregate (`SUM`, `AVG`, `COUNT`, `MIN`, `MAX`); anything else inside `MEASURE()` is an error. Inside `SELECT … FROM GRAPH_EXPAND("g")` the column list is the graph's `Label_column` names. > **Preview feature.** `MEASURE` and `GRAPH_EXPAND` are in BigQuery preview and may change before general availability. ## Converting JOINs to GQL and back Two code actions, in the right-click menu and the quick-fix lightbulb. Both need the graph's schema, which Querylab.io reads from your project. - **Convert to GQL GRAPH_TABLE syntax** is offered on a `SELECT` with at least one `JOIN` whose tables are node or edge tables of a known graph. - **Convert GRAPH_TABLE to standard SQL JOIN** is offered with the cursor inside a `GRAPH_TABLE(…)`, or a standalone `GRAPH …` query. **before** ```sql SELECT p1.name AS sender, p2.name AS receiver, t.amount FROM Person AS p1 JOIN Transfers AS t ON p1.id = t.source_id JOIN Person AS p2 ON t.dest_id = p2.id WHERE t.amount > 500 ORDER BY t.amount DESC LIMIT 100 ``` **after** ```sql SELECT * FROM GRAPH_TABLE( FinGraph MATCH (p1:Person)-[t:Transfers]->(p2:Person) WHERE t.amount > 500 RETURN p1.name AS sender, p2.name AS receiver, t.amount ORDER BY t.amount DESC LIMIT 100 ) ``` | SQL | GQL | | ------------------------------- | ----------------------- | | `FROM t1 JOIN t2 ON …` | `MATCH (n1)-[e]->(n2)` | | `LEFT JOIN` / `FULL OUTER JOIN` | `OPTIONAL MATCH` | | `WHERE cond` | `WHERE` / `FILTER cond` | | `SELECT col AS alias` | `RETURN col AS alias` | | `ORDER BY`, `LIMIT`, `OFFSET` | Carried through | Set operations, subqueries and `PIVOT` in the `FROM` clause, and CTEs other than a recursive path CTE don't convert; the action isn't offered. GQL → SQL keeps any trailing `WHERE`, `ORDER BY`, `LIMIT`, `OFFSET` unchanged. The same converter, with an editable schema DDL, runs without an account at [querylab.io/tools/gql-to-sql](https://querylab.io/tools/gql-to-sql). ## Keyword, label and property completion Keyword completions follow the statement: after `GRAPH name` you get `MATCH`; after a `MATCH`, the statements that can follow it; inside `()` and `[]` the label separator; after `RETURN` or `FILTER` the pattern variables. With **Settings → Editor → SQL Editor → Enable Graph Schema Completions** on (off by default), labels complete after the colon and properties after a variable's dot. Completions work without a reservation. Running a graph query needs an Enterprise or Enterprise Plus reservation. ```sql GRAPH FinGraph MATCH (p:Person)-[t:Transfers]->(q:Person) RETURN p. ``` ## Viewing results as a graph When a result column holds graph nodes or edges (`TO_JSON(n)` on a pattern variable), the result panel adds **Mode: Graph** next to Table and Json. ```sql SELECT TO_JSON(n) AS node, TO_JSON(e) AS edge, TO_JSON(m) AS target FROM GRAPH_TABLE( FinGraph MATCH (n:Person)-[e:Transfers]->(m:Person) RETURN n, e, m ) ``` **results → Mode: Graph** Transfers Transfers {[ [20, "Alex", "1", "34"], [220, "Dana", "3", "29"], [410, "Lee", "5", "41"], ].map(([x, name, id, age]) => ( Person {name} id: {id} age: {age} ))} Each card is a node: label, its identifying value, and up to three properties; click a node to see all of them. Edges carry their label on the line. Drag nodes to rearrange, scroll to zoom, drag the background to pan. Zoom and fit-to-view controls sit bottom-left; larger graphs get a minimap bottom-right.