圖表查詢總覽

本文將簡要介紹圖形查詢語言 (GQL),以及如何為 BigQuery Graph 編寫圖形查詢。您可以執行圖形查詢,找出模式、周遊關係,並從房源圖形取得洞察資訊。本文中的範例是指名為 FinGraph 的圖表,該圖表顯示人與其擁有的帳戶之間的關係,以及帳戶之間的轉移。如要瞭解圖表的定義,請參閱FinGraph範例

查詢結構

圖形查詢包含圖形的名稱,後方接著一或多個線性查詢陳述式。每個線性查詢都包含一或多個「陳述式」,可讓您處理圖表資料,找出模式比對、定義變數、篩選及轉換中繼資料,並傳回結果。您執行圖形查詢的方式,與在 BigQuery 中執行 SQL 查詢相同。

圖表查詢結構範例。
圖形查詢結構範例。

圖形模式比對

圖形模式比對:在圖形中找出特定模式。最基本的模式是元素模式,例如比對節點的節點模式,以及比對邊緣的邊緣模式。

節點模式

節點模式會比對圖表中的節點。這個模式包含相符的半形括號,其中可能含有圖表模式變數、標籤運算式和屬性篩選器。

找出所有節點

下列查詢會傳回圖表中的所有節點。變數 n 是圖形模式變數,會繫結至相符的節點。在本例中,節點模式會比對圖中的所有節點。

GRAPH graph_db.FinGraph
MATCH (n)
RETURN LABELS(n) AS label, n.id;

這項查詢會傳回 labelid

label id
Account 7
Account 16
Account 20
Person 1
Person 2
Person 3

找出具有特定標籤的所有節點

下列查詢會比對圖表中具有 Person 標籤的所有節點。查詢會傳回相符節點的標籤和部分屬性。

GRAPH graph_db.FinGraph
MATCH (p:Person)
RETURN LABELS(p) AS label, p.id, p.name;

這項查詢會傳回相符節點的下列屬性:

label id name
Person 1 Alex
Person 2 Dana
Person 3 Lee

找出符合標籤運算式的節點

您可以建立含有多個邏輯運算子的標籤運算式。舉例來說,下列查詢會比對圖表中的所有節點,這些節點具有 PersonAccount 標籤。圖形模式變數 n 會公開來自標示 PersonAccount 標籤的節點的所有屬性

GRAPH graph_db.FinGraph
MATCH (n:Person|Account)
RETURN LABELS(n) AS label, n.id, n.birthday, n.create_time;

請注意這項查詢的結果:

  • 所有節點都有 id 屬性。
  • 符合 Account 標籤的節點具有 create_time 屬性,但沒有 birthday 屬性。這些節點的 birthday 屬性為 NULL
  • 符合 Person 標籤的節點具有 birthday 屬性,但沒有 create_time 屬性。這些節點的 create_time 屬性為 NULL
label id birthday create_time
Account 7 NULL 2020-01-10T14:22:20.222Z
Account 16 NULL 2020-01-28T01:55:09.206Z
Account 20 NULL 2020-02-18T13:44:20.655Z
Person 1 1991-12-21T08:00:00Z NULL
Person 2 1980-10-31T08:00:00Z NULL
Person 3 1986-12-07T08:00:00Z NULL

找出符合標籤運算式和屬性篩選條件的所有節點

下列查詢會比對圖表中所有具有 Person 標籤,且屬性 id 等於 1 的節點:

GRAPH graph_db.FinGraph
MATCH (p:Person {id: 1})
RETURN LABELS(p) AS label, p.id, p.name, p.birthday;

結果大致如下:

label id name birthday
Person 1 Alex 1991-12-21T08:00:00Z

您可以使用 WHERE 子句,在標籤和屬性上形成更複雜的篩選條件。

下列查詢使用 WHERE 子句,篩選屬性 birthday 位於 1990-01-10 之前的節點:

GRAPH graph_db.FinGraph
MATCH (p:Person WHERE p.birthday < '1990-01-10')
RETURN LABELS(p) AS label, p.name, p.birthday;

結果大致如下:

label name birthday
Person Dana 1980-10-31T08:00:00Z
Person Lee 1986-12-07T08:00:00Z

邊緣模式

邊緣模式會比對節點之間的邊緣或關係。邊緣模式會以方括號 ([]) 括住,並包含 --><- 等符號,表示方向。邊緣模式可以選擇性地包含圖模式變數,以便繫結至相符的邊緣。

找出所有標籤相符的邊緣

這項查詢會傳回圖表中所有標籤為 Transfers 的邊緣。查詢會將圖形模式變數 e 繫結至相符的邊緣。

GRAPH graph_db.FinGraph
MATCH -[e:Transfers]->
RETURN e.Id as src_account, e.order_number;

結果大致如下:

src_account order_number
7 304330008004315
7 304120005529714
16 103650009791820
20 304120005529714
20 302290001255747

找出符合標籤運算式和屬性篩選條件的所有邊緣

下列查詢中的邊緣模式會使用標籤運算式和屬性篩選器,找出所有標示為 Transfers 的邊緣,這些邊緣符合指定的訂單編號:

GRAPH graph_db.FinGraph
MATCH -[e:Transfers {order_number: "304120005529714"}]->
RETURN e.Id AS src_account, e.order_number;

結果大致如下:

src_account order_number
7 304120005529714
20 304120005529714

使用任何方向的邊緣模式找出所有邊緣

您可以在查詢中使用 any direction 邊緣模式 (-[]-),比對任一方向的邊緣。下列查詢會找出所有帳戶遭封鎖的轉移作業:

GRAPH graph_db.FinGraph
MATCH (account:Account)-[transfer:Transfers]-(:Account {is_blocked:true})
RETURN transfer.order_number, transfer.amount;

結果大致如下:

order_number amount
304330008004315 300
304120005529714 100
103650009791820 300
302290001255747 200

路徑模式

路徑模式是由交替的節點和邊緣模式建構而成。

使用路徑模式找出特定節點的所有路徑

下列查詢會找出所有從 Person 擁有的帳戶發起的帳戶轉移,其中 id 等於 2

每個相符結果都代表從 Person 節點開始的路徑,當 id 等於 2 時,會透過使用 Owns 邊緣的已連線 Account 節點,進入使用 Transfers 邊緣的另一個 Account 節點。

GRAPH graph_db.FinGraph
MATCH
  (p:Person {id: 2})-[:Owns]->(account:Account)-[t:Transfers]->
  (to_account:Account)
RETURN
  p.id AS sender_id, account.id AS from_id, to_account.id AS to_id;

結果大致如下:

sender_id from_id to_id
2 20 7
2 20 16

量化路徑模式

量化模式會在指定範圍內重複模式。

比對量化邊緣模式

如要尋找路徑長度不一的邊緣,可以對邊緣模式套用量詞。下列查詢會找出與來源 Account 相距一到三次轉移的目標帳戶,且 id 值為 7,藉此說明這項功能。

查詢會將量詞 {1, 3} 套用至邊緣模式 -[e:Transfers]->。這會指示查詢比對重複 Transfers 邊緣模式一、二或三次的路徑。WHERE 子句用於從結果中排除來源帳戶。ARRAY_LENGTH 函式用於存取 group variable e

GRAPH graph_db.FinGraph
MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
WHERE src != dst
RETURN src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length, dst.id AS dst_account_id;

結果大致如下:

src_account_id path_length dst_account_id
7 1 16
7 1 16
7 3 16
7 3 16
7 2 20
7 2 20

結果中的部分資料列重複。這是因為在相同的來源和目的地節點之間,可能存在多個符合模式的路徑,而查詢會傳回所有路徑。

比對量化路徑模式

下列查詢會透過遭封鎖的中間帳戶,找出 Account 節點之間有一到兩個 Transfers 邊的節點。

括號中的路徑模式會經過量化,而其 WHERE 子句會指定重複模式的條件。

GRAPH graph_db.FinGraph
MATCH
  (src:Account)
  ((a:Account)-[:Transfers]->(b:Account {is_blocked:true}) WHERE a != b){1,2}
    -[:Transfers]->(dst:Account)
RETURN src.id AS src_account_id, dst.id AS dst_account_id;

結果大致如下:

src_account_id dst_account_id
7 20
7 20
20 20

群組變數

在量化模式中宣告的圖表模式變數,在該模式外存取時會變成群組變數。然後繫結至相符的圖形元素陣列。

您可以將群組變數視為陣列存取。系統會保留圖表元素,並按照這些元素在相符路徑中出現的順序排列。您可以使用水平匯總匯總群組變數。

存取權群組變數

在以下範例中,變數 e 的存取方式如下:

  • 當圖表模式變數繫結至 WHERE 子句中的單一邊緣時,e.amount > 100該變數位於量化模式內。
  • 當群組變數繫結至 RETURN 陳述式中邊緣元素陣列時,如果該變數位於量化模式之外,則會繫結至 ARRAY_LENGTH(e)
  • 做為繫結至邊緣元素陣列的群組變數,該陣列是由量化模式外的 SUM(e.amount) 彙整而成。這是水平匯總的範例。
GRAPH graph_db.FinGraph
MATCH
  (src:Account {id: 7})-[e:Transfers WHERE e.amount > 100]->{0,2}
  (dst:Account)
WHERE src.id != dst.id
LET total_amount = SUM(e.amount)
RETURN
  src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length,
  total_amount, dst.id AS dst_account_id;

結果大致如下:

src_account_id path_length total_amount dst_account_id
7 1 300 16
7 2 600 20

路徑搜尋前置字串

如要限制共用來源和目的地節點的群組內相符路徑,可以使用 ANYANY SHORTESTANY CHEAPEST 路徑搜尋前置字元。您只能在整個路徑模式前套用這些前置字串,且無法在括號內套用。

使用 ANY 比對

以下查詢會找出與指定 Account 節點相距一或兩個 Transfers 的所有可連線專屬帳戶。

ANY 路徑搜尋前置字串可確保查詢只會傳回一組不重複的 srcdst Account 節點之間的路徑。在下列範例中,雖然您可以透過來源 Account 節點的兩個不同路徑,抵達 Account 節點,但查詢只會傳回一個路徑。{id: 16}

GRAPH graph_db.FinGraph
MATCH ANY (src:Account {id: 7})-[e:Transfers]->{1,2}(dst:Account)
LET ids_in_path = ARRAY_CONCAT(ARRAY_AGG(e.Id), [dst.Id])
RETURN src.id AS src_account_id, dst.id AS dst_account_id, ids_in_path;

結果大致如下:

src_account_id dst_account_id ids_in_path
7 16 7,16
7 20 7,16,20

使用 ANY SHORTEST 比對

ANY SHORTEST 路徑搜尋前置碼會為每對來源和目的地節點傳回單一路徑,並從邊緣數量最少的路徑中選取。

舉例來說,下列查詢會找出 Account 值為 7Account 節點,以及 id 值為 20Account 節點之間,其中一條最短路徑。id查詢會考量有一到三個 Transfers 邊緣的路徑。

GRAPH graph_db.FinGraph
MATCH ANY SHORTEST (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account {id: 20})
RETURN src.id AS src_account_id, dst.id AS dst_account_id, ARRAY_LENGTH(e) AS path_length;

結果大致如下:

src_account_id dst_account_id path_length
7 20 2

使用 ANY CHEAPEST 比對

ANY CHEAPEST 路徑搜尋前置字元可確保查詢只會針對每對來源和目的地帳戶,傳回總運算成本最低的路徑。

下列查詢會找出 Account 節點之間總運算成本最低的路徑。這項費用是根據邊緣的 amount 屬性總和計算而得。Transfers搜尋會考量有一到三個 Transfers 邊的路徑。

GRAPH graph_db.FinGraph
MATCH ANY CHEAPEST (src:Account)-[e:Transfers COST e.amount]->{1,3}(dst:Account)
LET total_cost = sum(e.amount)
RETURN src.id AS src_account_id, dst.id AS dst_account_id, total_cost;

結果大致如下:

src_account_id dst_account_id total_cost
7 7 900
7 16 100
7 20 400
16 7 800
16 16 500
16 20 300
20 7 500
20 16 200
20 20 500

圖形模式

圖形模式由一或多個路徑模式組成,並以半形逗號 (,) 分隔。圖形模式可以包含 WHERE 子句,讓您存取路徑模式中的所有圖形模式變數,以形成篩選條件。每個路徑模式都會產生路徑集合。

使用圖形模式比對

下列查詢會找出涉及交易金額超過 200 元的中介帳戶及其擁有者,這些帳戶會將資金從來源帳戶轉移至遭封鎖的帳戶。

下列路徑模式會形成圖表模式:

  • 第一個模式會找出路徑,其中轉移作業是透過中繼帳戶,從一個帳戶轉移至遭封鎖的帳戶。
  • 第二種模式會找出從帳戶到擁有者的路徑。

變數 interm 可做為兩個路徑模式之間的共同連結,因此 interm 必須在兩個路徑模式中參照相同的元素節點。這會根據 interm 變數建立等值聯結作業。

GRAPH graph_db.FinGraph
MATCH
  (src:Account)-[t1:Transfers]->(interm:Account)-[t2:Transfers]->(dst:Account),
  (interm)<-[:Owns]-(p:Person)
WHERE dst.is_blocked = TRUE AND t1.amount > 200 AND t2.amount > 200
RETURN
  src.id AS src_account_id, dst.id AS dst_account_id,
  interm.id AS interm_account_id, p.id AS owner_id;

結果大致如下:

src_account_id dst_account_id interm_account_id owner_id
20 16 7 1

線性查詢陳述式

您可以將多個圖表陳述式串連在一起,形成線性查詢陳述式。系統會按照查詢中的陳述式順序執行。

  • 每個陳述式都會將前一個陳述式的輸出做為輸入。第一個陳述式的輸入內容為空白。

  • 最後一個陳述式的輸出就是最終結果。

舉例來說,您可以使用線性查詢陳述式,找出轉移至遭封鎖帳戶的最高金額。下列查詢會找出帳戶,以及轉移至遭封鎖帳戶的轉出金額最高的擁有者。

GRAPH graph_db.FinGraph
MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account {is_blocked:true})
ORDER BY transfer.amount DESC
LIMIT 1
MATCH (src_account:Account)<-[owns:Owns]-(owner:Person)
RETURN src_account.id AS account_id, owner.name AS owner_name;

下表說明這個程序,顯示在每個陳述式之間傳遞的中間結果。為簡潔起見,這裡只顯示部分屬性。

陳述式 中繼結果 (簡短)
MATCH
  (src_account:Account)
    -[transfer:Transfers]->
  (dst_account:Account {is_blocked:true})
src_account 轉移 dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}
{id: 7} {amount: 100.0} {id: 16, is_blocked: true}
{id: 20} {amount: 200.0} {id: 16, is_blocked: true}

ORDER BY transfer.amount DESC
src_account 轉移 dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}
{id: 20} {amount: 200.0} {id: 16, is_blocked: true}
{id: 7} {amount: 100.0} {id: 16, is_blocked: true}

LIMIT 1
src_account 轉移 dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}

MATCH
  (src_account:Account)
    <-[owns:Owns]-
  (owner:Person)
src_account 轉移 dst_account 擁有 擁有者
{id: 7} {amount: 300.0} {id: 16, is_blocked: true} {person_id: 1, account_id: 7} {id: 1, name: Alex}
RETURN
  src_account.id AS account_id,
  owner.name AS owner_name
        
account_id owner_name
7 Alex

結果大致如下:

account_id owner_name
7 Alex

回傳敘述

RETURN 陳述式會指定要從相符模式傳回的內容。可存取圖形模式變數,並包含運算式和其他子句,例如 ORDER BYGROUP BY

BigQuery Graph 不支援將圖表元素做為查詢結果傳回。如要傳回整個圖表元素,請使用 TO_JSON 函式

以 JSON 格式傳回圖表元素

GRAPH graph_db.FinGraph
MATCH (n:Account {id: 7})
-- Returning a graph element in the final results is NOT allowed. Instead, use
-- the TO_JSON function or explicitly return the graph element's properties.
RETURN TO_JSON(n) AS n;

結果大致如下:

n
{"identifier":"mUZpbkdyYXBoLkFjY291bnQAeJEO","kind":"node","labels":["Account"],"properties":{"create_time":"2020-01-10T14:22:20.222Z","id":7,"is_blocked":false,"nick_name":"Vacation Fund"}}

使用 NEXT 關鍵字撰寫較大的查詢

您可以使用 NEXT 關鍵字,串連多個圖表線性查詢陳述式。第一個陳述式會收到空白輸入內容,後續每個陳述式的輸出內容則會成為下一個陳述式的輸入內容。

以下範例會透過串連多個圖表線性陳述式,找出收到最多轉入款項的帳戶擁有者。您可以使用相同變數 (例如 account),在多個線性陳述式中參照相同圖表元素。

GRAPH graph_db.FinGraph
MATCH (:Account)-[:Transfers]->(account:Account)
RETURN account, COUNT(*) AS num_incoming_transfers
GROUP BY account
ORDER BY num_incoming_transfers DESC
LIMIT 1

NEXT

MATCH (account:Account)<-[:Owns]-(owner:Person)
RETURN account.id AS account_id, owner.name AS owner_name, num_incoming_transfers;

結果大致如下:

account_id owner_name num_incoming_transfers
16 Lee 3

您也可以使用集合運算子合併線性查詢陳述式

函式和運算式

您可以在圖形查詢中使用所有 GoogleSQL 函式 (包括匯總和純量函式)、運算子條件運算式。BigQuery Graph 也支援 GQL 函式GQL 運算子,這些函式和運算子只能用於圖形查詢。

下列查詢在圖形查詢中混合使用 GQL 和 SQL 函式與運算子:

GRAPH graph_db.FinGraph
MATCH (person:Person)-[o:Owns]->(account:Account)
WHERE person IS SOURCE OF o
RETURN person, ARRAY_AGG(account.nick_name) AS accounts
GROUP BY person

NEXT

RETURN
  LABELS(person) AS labels,
  accounts,
  CONCAT(person.city, ", ", person.country) AS location,
  TO_JSON(person) AS person
LIMIT 1;

結果大致如下:

標籤 帳戶 位置 使用者圖示
Person ["Vacation Fund"] Adelaide, Australia {"identifier":"mUZpbkdyYXBoLlBlcnNvbgB4kQI=","kind":"node","labels":["Person"],"properties":{"birthday":"1991-12-21T08:00:00Z","city":"Adelaide","country":"Australia","id":1,"name":"Alex"}}

子查詢

「子查詢」是指巢狀查詢,下列規則適用於圖表查詢中的子查詢:

  • 子查詢會以一對大括號 {} 括住。
  • 子查詢會以 GRAPH 子句開頭,指定範圍內的圖形。指定的圖表不必與外部查詢中使用的圖表相同。
  • 在子查詢範圍外宣告的圖形模式變數無法在子查詢內重複宣告,但可在子查詢內的運算式或函式中參照。

使用子查詢找出每個帳戶的轉移總數

下列查詢說明如何使用 VALUE 子查詢。子查詢會以大括號 {} 括住,並以 VALUE 關鍵字做為前置字串。這項查詢會傳回從帳戶發起的轉移總數。

GRAPH graph_db.FinGraph
MATCH (p:Person)-[:Owns]->(account:Account)
RETURN p.name, account.id AS account_id, VALUE {
  GRAPH graph_db.FinGraph
  MATCH (a:Account)-[transfer:Transfers]->(:Account)
  WHERE a = account
  RETURN COUNT(transfer) AS num_transfers
} AS num_transfers;

結果大致如下:

名稱 account_id num_transfers
Alex 7 2
Dana 20 2
Lee 16 1

如需支援的子查詢運算式清單,請參閱 BigQuery Graph 子查詢

查詢參數

您可以透過參數查詢 BigQuery 圖表。詳情請參閱語法,並瞭解如何執行參數化查詢

下列查詢會比對 Person 節點,這些節點的 id 值符合查詢參數:

GRAPH graph_db.FinGraph
MATCH (person:Person {id: @id})
RETURN person.name;

同時查詢圖表和表格

您可以使用 GRAPH_TABLE 運算子,合併圖形查詢和 SQL 查詢。

GRAPH_TABLE 運算子會接受線性圖形查詢,並以表格形式傳回結果,以便整合至 SQL 查詢。這項互通性可讓您使用非圖表內容擴充圖表查詢結果,反之亦然。

舉例來說,您可以建立 CreditReports 資料表並插入幾份信用報告,如下列範例所示:

CREATE TABLE graph_db.CreditReports (
  person_id     INT64 NOT NULL,
  create_time   TIMESTAMP NOT NULL,
  score         INT64 NOT NULL,
  PRIMARY KEY (person_id, create_time) NOT ENFORCED
);
INSERT INTO graph_db.CreditReports (person_id, create_time, score)
VALUES
  (1,"2020-01-10 06:22:20.222", 700),
  (2,"2020-02-10 06:22:20.222", 800),
  (3,"2020-03-10 06:22:20.222", 750);

接著,您可以在 GRAPH_TABLE 中透過圖模式比對找出特定人員,並將圖查詢結果與 CreditReports 資料表合併,以擷取信用評分。

SELECT
  gt.person.id,
  credit.score AS latest_credit_score
FROM GRAPH_TABLE(
  graph_db.FinGraph
  MATCH (person:Person)-[:Owns]->(:Account)-[:Transfers]->(account:Account {is_blocked:true})
  RETURN DISTINCT person
) AS gt
JOIN graph_db.CreditReports AS credit
  ON gt.person.id = credit.person_id
ORDER BY credit.create_time;

結果大致如下:

person_id latest_credit_score
1 700
2 800

後續步驟