圖表查詢總覽

BigQuery Graph 可讓您編寫圖譜查詢,比對節點、邊緣和路徑模式,並使用Graph Query Language (GQL) 掃遍關係。本文件中的範例參照名為 FinGraph 的圖表,其中顯示人員、他們擁有的帳戶,以及帳戶之間的轉帳關係。如要瞭解圖表的定義,請參閱 FinGraph 範例。

查詢結構

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

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

圖表模式比對

圖譜模式比對:在圖譜中尋找特定模式。最基本的模式是元素模式,例如與節點相符的節點模式,以及與邊相符的邊模式。

節點模式

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

找出所有節點

下列查詢會傳回圖表中的所有節點。圖表模式變數 n 會繫結至相符的節點。在這種情況下,節點模式會比對圖表中的所有節點。

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

這項查詢會傳回 label 和 id:

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

找出與標籤運算式相符的所有節點

您可以使用一或多個邏輯運算子建立標籤運算式。舉例來說,下列查詢會比對圖表中所有具有 Person 或 Account 標籤的節點。圖表模式變數 n 會公開具有 Person 或 Account 標籤的節點的所有屬性。

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) 發起的所有帳戶轉帳。

每個相符結果都代表一條路徑,從 id 等於 2 的 Person 節點,透過使用 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

路徑搜尋前置字串

如要限制共用來源和目的地節點的群組內相符路徑,可以使用 ANY、ANY SHORTEST 或 ANY CHEAPEST 路徑搜尋前置字元。這些前置字元只能套用在完整路徑模式之前,不能套用在括號內。

使用 ANY 進行比對

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

ANY 路徑搜尋前置字元可確保查詢只會傳回一組不重複 src 和 dst Account 節點之間的一條路徑。在以下範例中,雖然您可以透過來源 Account 節點的兩個不同路徑,使用 {id: 16} 連線至 Account 節點,但查詢只會傳回一個路徑。

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 路徑搜尋前置字元會為每組來源和目的地節點傳回單一路徑,並從邊緣數量最少的路徑中選取。

舉例來說,下列查詢會找出 id 值為 7 的 Account 節點,與 id 值為 20 的 Account 節點之間,其中一條最短路徑。查詢會考量具有一到三個 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 節點之間總運算成本最低的路徑。這項費用是根據 Transfers 邊緣的 amount 屬性總和計算。搜尋會考慮具有一到三個 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 transfer 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 transfer 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 transfer dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}

MATCH
  (src_account:Account)
    <-[owns:Owns]-
  (owner:Person)
src_account transfer dst_account owns owner
{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 BY 和 GROUP 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 Graph。詳情請參閱語法,並瞭解如何執行參數化查詢。

下列查詢會比對 id 值與查詢參數相符的 Person 節點:

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

後續步驟