本頁說明如何在 Spanner 圖中管理無結構定義資料。也詳細說明最佳做法和疑難排解提示。建議您熟悉 Spanner Graph 結構定義和查詢。
無結構定義資料管理功能可讓您建立靈活的圖表定義,在這種情況下,您可以新增、更新或刪除節點和邊類型定義,而無須變更結構定義。這種做法可支援逐步開發作業,並減少結構定義管理負擔,同時保留熟悉的圖表查詢體驗。
無架構資料管理功能特別適合用於下列情境:
- 您管理的圖表經常變更,例如更新和新增元素標籤和屬性。
- 您的圖表包含許多節點和邊緣類型,因此建立及管理輸入資料表相當困難。
以模型模擬無結構定義資料
您可以使用 Spanner Graph,根據資料表建立圖形,其中資料列會對應至節點和邊緣。無結構化資料建模通常會使用單一節點資料表和單一邊緣資料表,其中 STRING
欄用於標籤,JSON
欄用於屬性,而非為每個元素類型使用個別的資料表。
建立輸入表格
您可以建立單一 GraphNode
資料表和單一 GraphEdge
資料表,用於儲存無結構定義資料,如以下範例所示。表格名稱僅供說明,您可以自行選擇。
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
這個範例會執行以下操作:
將所有節點儲存在單一資料表
GraphNode
中,並由id
做為專屬識別碼。將所有邊儲存在單一資料表
GraphEdge
中,並透過來源 (id
)、目的地 (dest_id
) 和自身 ID (edge_id
) 組合來唯一識別。edge_id
會納入主鍵,以便允許從id
到dest_id
的組合有多個邊。
節點和邊資料表都各自有 STRING
和 JSON
類型的 label
和 properties
資料欄。
建立資源圖表
使用 CREATE PROPERTY GRAPH 陳述式時,上一個章節中的輸入資料表會對應為節點和邊。您必須使用下列子句,為無結構資料定義標籤和屬性:
DYNAMIC LABEL
:從輸入資料表的STRING
資料欄建立節點或邊緣的標籤。DYNAMIC PROPERTIES
:從輸入資料表的JSON
資料欄建立節點或邊的屬性。
以下範例說明如何使用這些子句建立圖表:
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
動態標籤
DYNAMIC LABEL
子句會指定 STRING
資料類型資料欄,用於儲存標籤值。
舉例來說,如果 GraphNode
資料列中的 label
欄有 person
值,就會對應至圖表中的 Person
節點。同樣地,在 GraphEdge
列中,如果標籤欄的值為 owns
,則會對應至圖表中的 Owns
邊緣。
動態屬性
DYNAMIC PROPERTIES
子句會指定 JSON
資料類型資料欄,用於儲存屬性。JSON 鍵是屬性名稱,JSON 值是屬性值。
舉例來說,如果 GraphNode
資料列的 properties
欄位含有 JSON 值 '{"name": "David", "age": 43}'
,就會對應至含有 age
和 name
屬性的節點,其中 43
和 "David"
為屬性值。
不應使用無結構化資料管理功能的情況
在下列情況下,您可能不想使用無結構定義資料管理功能:
- 圖資料的節點和邊類型已明確定義,或其標籤和屬性不需要頻繁更新。
- 您的資料已儲存在 Spanner 中,您偏好從現有資料表建立圖表,而非引入新的專屬節點和邊資料表。
- 無結構化資料的限制會阻礙採用。
此外,如果工作負載對寫入效能極為敏感,特別是在屬性經常更新的情況下,使用架構定義的屬性搭配 STRING
或 INT64
等原始資料類型,比使用含有 JSON
類型的動態屬性更有效率。
如要進一步瞭解如何在不使用動態資料標籤和屬性的情況下定義圖表結構定義,請參閱 Spanner Graph 結構定義總覽。
查詢無結構定義的圖形資料
您可以使用Graph Query Language (GQL) 查詢無結構定義的圖形資料。您可以使用 Spanner 圖形查詢總覽和 GQL 參考資料中的查詢範例,並進行有限修改。
使用標籤比對節點和邊緣
您可以使用 GQL 中的標籤運算式比對節點和邊。
以下查詢會比對標籤欄中值為 account
和 transfers
的連結節點和邊。
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
存取屬性
JSON
資料類型的頂層鍵和值會以屬性模擬,例如下例中的 age
和 name
。
JSON document |
Properties |
|
|
|
以下範例說明如何從 Person
節點存取屬性 name
。
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
這麼做會傳回類似以下的結果:
JSON"Tom"
轉換房源資料類型
系統會將屬性視為 JSON 資料類型的值。在某些情況下,例如與 SQL 類型進行比較時,就需要先將這些類型轉換為 SQL 類型。
在以下範例中,查詢會執行下列資料類型轉換:
- 將
is_blocked
屬性轉換為布林值類型,以便評估運算式。 - 將
order_number_str
屬性轉換為字串類型,並與常值"302290001255747"
進行比較。 - 使用 LAX_INT64 函式,將
order_number_str
安全地轉換為整數,做為傳回類型。
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE BOOL(a.is_blocked) AND STRING(t.order_number_str) = "302290001255747"
RETURN LAX_INT64(t.order_number_str) AS order_number_as_int64;
這麼做會傳回類似以下的結果:
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
在 GROUP BY
和 ORDER BY
等子句中,您也需要轉換 JSON 資料類型。以下範例會將 city
屬性轉換為字串類型,以便用於分組。
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
將 JSON 資料類型轉換為 SQL 資料類型的訣竅:
- 嚴格轉換器 (例如
INT64
) 會進行嚴格的類型和值檢查。建議您在已知並強制執行 JSON 資料類型時使用此方法,例如使用結構定義限制來強制執行屬性資料類型。 - 彈性轉換器 (例如
LAX_INT64
) 會盡可能安全地轉換值,並在無法轉換時傳回NULL
。如果不需要嚴格檢查,或難以強制執行類型,建議採用這種做法。
如要進一步瞭解資料轉換,請參閱疑難排解提示。
依屬性值篩選
在屬性篩選器中,系統會將篩選器參數視為 JSON
資料類型的值。舉例來說,在下列查詢中,is_blocked
會視為 JSON boolean
,order_number_str
則視為 JSON string
。
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
這麼做會傳回類似以下的結果:
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
篩選器參數必須與屬性類型和值相符。舉例來說,當 order_number_str
篩選器參數為整數時,由於屬性為 JSON string
,因此不會找到任何相符項目。
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
存取巢狀 JSON 屬性
巢狀 JSON 鍵和值不會以屬性建模。在以下範例中,JSON 鍵 city
、state
和 country
並未模擬為屬性,因為它們已在 location
下巢狀結構化。不過,您可以使用 JSON 欄位存取運算子或 JSON 下標運算子存取這些欄位。
JSON document |
Properties |
|
|
|
|
|
以下範例說明如何使用 JSON 欄位 存取運算子存取巢狀屬性。
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
這麼做會傳回類似以下的結果:
"New York"
修改無結構定義資料
Spanner Graph 會將資料從表格對應至圖形節點和邊緣。變更輸入表格資料時,系統會直接對應對應的圖表資料進行突變。如要進一步瞭解圖表資料變異,請參閱「插入、更新或刪除 Spanner 圖表資料」。
範例
本節提供建立、更新及刪除圖表資料的範例。
插入圖表資料
以下範例會插入 person
節點。標籤和屬性名稱必須使用小寫字母。
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
更新圖表資料
以下範例會更新 Account
節點,並使用 JSON_SET
函式設定其 is_blocked
屬性。
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
以下範例會使用新一組屬性更新 person
節點。
UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;
以下範例使用 JSON_REMOVE
函式,從 Account
節點移除 is_blocked
屬性。執行後,所有其他現有資源都會保持不變。
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
刪除圖譜資料
以下範例會刪除已轉移至封鎖帳戶的 Account
節點上的 Transfers
邊。
DELETE FROM GraphEdge
WHERE label = "transfers" and id IN {
GRAPH FinGraph
MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
RETURN a.id
}
限制
本節列出使用無結構化資料管理的限制。
動態標籤的單一資料表規定
如果定義中使用動態標籤,則只能有一個節點資料表。這項限制也適用於邊緣資料表。以下行為皆屬違規:
- 定義節點資料表時,可使用動態標籤搭配任何其他節點資料表。
- 定義邊緣資料表時,可使用動態標籤和其他邊緣資料表。
- 定義多個節點資料表或多個邊資料表,每個資料表都使用動態標籤。
舉例來說,下列程式碼嘗試建立多個含有動態標籤的圖形節點時會失敗。
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNodeOne
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
GraphNodeTwo
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
Account
LABEL Account PROPERTIES(create_time)
)
EDGE TABLES (
...
);
標籤名稱必須為小寫
標籤字串值必須儲存為小寫,才能進行比對。建議您在應用程式程式碼中或使用結構定義限制強制執行此規則。
雖然標籤字串值必須以小寫儲存,但在查詢中引用時,標籤字串值不區分大小寫。
以下範例說明如何插入小寫值的標籤:
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
您可以使用不區分大小寫的標籤來比對 GraphNode
或 GraphEdge
。
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
屬性名稱必須以小寫英文字母開頭
屬性名稱必須以小寫儲存。建議您在應用程式程式碼中或使用結構定義限制強制執行此規則。
雖然屬性名稱必須以小寫儲存,但在查詢中參照屬性名稱時,系統不會區分大小寫。
以下範例會插入使用小寫字母的 name
和 age
屬性。
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
在查詢文字中,屬性名稱不區分大小寫。舉例來說,您可以使用 Age
或 age
存取資源。
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
其他限制
最佳做法
本節將說明建構無結構定義資料的最佳做法。
節點和邊的主鍵定義
節點的鍵在所有圖形節點中均不得重複。例如 INT64
或字串 UUID
資料欄。
如果兩個節點之間有多個邊,您必須為邊引入專屬 ID。結構定義範例使用應用程式邏輯 INT64
edge_id
欄。
建立節點和邊資料表的結構定義時,如果值無法變更,您可以選擇將 label
資料欄納入做為主鍵資料欄。如此一來,由所有鍵欄所組成的複合鍵,在所有節點或邊緣中都應是唯一的。這項技巧可改善只依標籤篩選的查詢效能。
如要進一步瞭解如何選擇主鍵,請參閱「選擇主鍵」。
經常存取的資源的次要索引
如要針對篩選器中常用的屬性提升查詢效能,您可以針對產生的屬性欄建立次要索引,然後在圖形結構定義和查詢中使用該索引。
以下範例會將產生的 age
資料欄新增至 person
節點的 GraphNode
資料表。如果節點沒有 person
標籤,則值為 NULL
。
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
接著,它會為 person_age
建立 NULL FILTERED INDEX
,並將其交錯插入 GraphNode
資料表,以便本機存取。
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
GraphNode
表格現在包含可做為圖形節點屬性的新欄。如要在資源圖定義中反映這項資訊,請使用 CREATE OR REPLACE PROPERTY GRAPH
陳述式。這會重新編譯定義,並將新的 person_age
資料欄納入為屬性。
以下陳述式會重新編譯定義,並將新的 person_age
資料欄納入屬性。
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode (id)
DESTINATION KEY (dest_id) REFERENCES GraphNode (id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
以下範例會使用已編入索引的屬性執行查詢。
GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;
您可以選擇在建立索引後執行 ANALYZE
指令,讓查詢最佳化工具更新至最新的資料庫統計資料。
檢查資料完整性的限制條件
Spanner 支援檢查限制等結構定義物件,以便強制執行標籤和屬性資料完整性。本節列出可與無結構定義資料搭配使用的檢查限制建議。
強制使用有效的標籤值
建議您在標籤欄定義中使用 NOT NULL
,以免出現未定義的標籤值。
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
強制使用小寫的標籤值和屬性名稱
由於標籤和屬性名稱必須儲存為小寫值,建議您執行下列任一操作:
- 在應用程式邏輯中強制執行檢查。
- 在結構定義中建立檢查限制。
在查詢時,標籤和屬性名稱不區分大小寫。
以下範例會在 GraphNode
資料表中新增節點標籤限制,確保標籤為小寫。
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
以下範例會在邊緣屬性名稱中加入檢查限制。檢查會使用 JSON_KEYS
存取頂層鍵。如果 JSON_KEYS
傳回 NULL
,COALESCE
會將輸出內容轉換為空 آرایه و檢查每個鍵是否為小寫。
ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
強制執行屬性存在
您可以建立限制條件,檢查標籤是否存在屬性。
在以下範例中,限制會檢查 person
節點是否具有 name
屬性。
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
強制執行資源唯一性
您可以建立以屬性為基礎的限制條件,檢查節點或邊的屬性是否在具有相同標籤的節點或邊中不重複。如要這樣做,請針對資源的產生欄使用 UNIQUE INDEX。
在以下範例中,不重複索引會檢查 name
和 country
屬性組合是否為任何 person
節點的不重複值。
為
PersonName
新增產生的資料欄。ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
為
PersonCountry
新增產生的資料欄。ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
針對
PersonName
和PersonCountry
屬性建立NULL_FILTERED
專屬索引。CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
強制執行屬性資料類型
您可以對標籤的屬性值使用資料類型限制,強制執行屬性資料類型,如以下範例所示。這個範例使用 JSON_TYPE
函式,檢查 person
標籤的 name
屬性是否使用 STRING
類型。
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
結合定義和動態標籤
Spanner 可讓資源圖中的節點同時具有定義的標籤 (在結構定義中定義) 和動態標籤 (衍生自資料)。您可以自訂標籤,以便運用這項彈性功能。
請參考下列結構定義,瞭解如何建立 GraphNode
資料表:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
LABEL Entity -- Defined label
DYNAMIC LABEL (label) -- Dynamic label from data column 'label'
DYNAMIC PROPERTIES (properties)
);
在此,從 GraphNode
建立的每個節點都具有定義的標籤 Entity
。此外,每個節點都有一個動態標籤,取決於標籤欄中的值。
接著,您可以根據任一標籤類型撰寫查詢,以便比對節點。舉例來說,下列查詢會使用定義的 Entity
標籤來尋找節點:
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
雖然這項查詢會使用已定義的標籤 Entity
,但請注意,相符節點也會根據其資料攜帶動態標籤。
結構定義範例
您可以使用本節的架構範例做為範本,自行建立架構。主要結構定義元件包括:
以下範例說明如何建立輸入資料表和資源圖:
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
以下範例會使用索引改善反向邊緣的檢索作業。STORING (properties)
子句包含邊緣屬性的副本,可加快篩選這些屬性的查詢速度。如果查詢不需要使用 STORING (properties)
子句,您可以省略該子句。
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
以下範例使用標籤索引,加快依標籤比對節點的速度。
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
以下範例會新增強制使用小寫字母的標籤和屬性的限制條件。最後兩個範例使用 JSON_KEYS
函式。您也可以選擇在應用程式邏輯中強制執行小寫檢查。
ALTER TABLE GraphNode ADD CONSTRAINT node_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphEdge ADD CONSTRAINT edge_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphNode ADD CONSTRAINT node_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
ALTER TABLE GraphEdge ADD CONSTRAINT edge_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
使用 DML 最佳化動態屬性的批次更新
使用 JSON_SET
和 JSON_REMOVE
等函式修改動態屬性時,會涉及讀取-修改-寫入作業。與更新 STRING
或 INT64
類型的資源相比,這類資源可能會導致較高的費用。
如果您的工作負載涉及使用 DML 對動態資源進行批次更新,請考慮採用下列最佳化建議,以提升效能:
在單一 DML 陳述式中更新多個資料列,而非個別處理資料列。
更新廣泛的索引鍵範圍時,請依主索引鍵將受影響的資料列分組及排序。使用每個 DML 更新不重疊的範圍,可減少鎖定競爭。
在 DML 陳述式中使用查詢參數,而非硬式編碼,以便提升效能。
根據這些建議,以下範例會在單一 DML 陳述式中更新 100 個節點的 is_blocked
屬性。查詢參數包括:
@node_ids
:GraphNode
資料列的鍵,儲存在ARRAY
參數中。如適用,將這些資料以 DML 分組及排序,可獲得更佳的成效。@is_blocked_values
:要更新的對應值,儲存在ARRAY
參數中。
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked',
CASE id
WHEN @node_ids[OFFSET(0)] THEN @is_blocked_values[OFFSET(0)]
WHEN @node_ids[OFFSET(1)] THEN @is_blocked_values[OFFSET(1)]
...
WHEN @node_ids[OFFSET(99)] THEN @is_blocked_values[OFFSET(99)]
END,
create_if_missing => TRUE)
WHERE id IN UNNEST(@node_ids)
疑難排解
本節說明如何排解無結構化資料的問題。
屬性在 TO_JSON
結果中重複出現
問題
下列節點會將 birthday
和 name
屬性模擬為 JSON
欄中的動態屬性。圖形元素 JSON 結果中出現 birthday
和 name
的重複屬性。
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
這麼做會傳回類似以下的結果:
{
…,
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 14,
"label": "person",
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex"
}
}
…
}
可能的原因
根據預設,主資料表的所有欄都會定義為屬性。使用 TO_JSON
或 SAFE_TO_JSON
傳回圖表元素會導致重複的屬性。這是因為 JSON
資料欄 (即 properties
) 是結構定義的屬性,而 JSON
的第一層鍵則以動態屬性建模。
建議解決方案
為避免這種行為,請在定義結構定義中的屬性時,使用 PROPERTIES ALL COLUMNS EXCEPT
子句排除 properties
欄,如以下範例所示:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
結構定義變更後,JSON
資料類型的傳回圖形元素就不會重複。
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
這個查詢會傳回以下內容:
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
屬性值未正確轉換時的常見問題
解決下列問題的常見方法,就是在查詢運算式中使用屬性時,一律使用屬性值轉換。
不考量轉換的屬性值比較
問題
No matching signature for operator = for argument types: JSON, STRING
可能的原因
查詢無法正確轉換屬性值。舉例來說,在比較時,name
屬性不會轉換為 STRING
類型:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
建議解決方案
如要修正這個問題,請在比較前使用值轉換。
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
這麼做會傳回類似以下的結果:
+------+
| id |
+------+
| 1 |
+------+
或者,您也可以使用屬性篩選器,在自動完成值轉換的情況下簡化相等比較作業。請注意,值的類型 (「Alex」) 必須與 JSON
中的屬性 STRING
類型完全相符。
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
這麼做會傳回類似以下的結果:
+------+
| id |
+------+
| 1 |
+------+
RETURN DISTINCT
屬性值使用方式 (不含轉換)
問題
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
可能的原因
在以下範例中,order_number_str
在 RETURN DISTINCT
陳述式中使用前並未經過轉換:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
建議解決方案
如要修正這個問題,請在 RETURN DISTINCT
前使用值轉換。
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
這麼做會傳回類似以下的結果:
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
用於分組但未轉換的屬性
問題
Grouping by expressions of type JSON is not allowed.
可能的原因
在以下範例中,t.order_number_str
會在用於群組 JSON 物件之前進行轉換:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
建議解決方案
如要修正這個問題,請先使用值轉換,再將屬性用作分組鍵。
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
這麼做會傳回類似以下的結果:
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
用於排序的屬性,不含轉換
問題
ORDER BY does not support expressions of type JSON
可能的原因
在以下範例中,t.amount
會先轉換,再用於排序結果:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY t.amount DESC
LIMIT 1;
建議解決方案
如要修正這個問題,請對 ORDER BY
子句中的 t.amount
執行轉換。
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY DOUBLE(t.amount) DESC
LIMIT 1;
這麼做會傳回類似以下的結果:
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
轉換期間的類型不符
問題
The provided JSON input is not an integer
可能的原因
在以下範例中,order_number_str
屬性會儲存為 JSON STRING
資料類型。如果您嘗試將其轉換為 INT64
,系統會傳回錯誤。
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
建議解決方案
如要修正這個問題,請使用與值類型相符的確切值轉換器。
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
這麼做會傳回類似以下的結果:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
或者,如果值可轉換為目標類型,請使用彈性轉換器,如以下範例所示:
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
這麼做會傳回類似以下的結果:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
後續步驟
- 如要進一步瞭解 JSON,請參閱「修改 JSON 資料」和「JSON 函式清單」。
- 比較 Spanner Graph 和 openCypher。
- 遷移至 Spanner Graph。