このページでは、Spanner Graph 内でスキーマレス データを管理する方法について説明します。また、ベスト プラクティスとトラブルシューティングのヒントについても説明します。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
)、独自の識別子(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 に保存されており、新しい専用のノードテーブルとエッジテーブルを導入するのではなく、既存のテーブルからグラフを構築したい。
- スキーマレス データの制限により、導入が妨げられている。
また、ワークロードが書き込みパフォーマンスの影響を受けやすい場合(特にプロパティが頻繁に更新される場合)、JSON
型の動的プロパティを使用するよりも、STRING
や INT64
などのプリミティブ データ型でスキーマ定義プロパティを使用するほうが効果的です。
動的データラベルとプロパティを使用せずにグラフスキーマを定義する方法については、Spanner Graph スキーマの概要をご覧ください。
スキーマレス グラフデータをクエリする
スキーマレスのグラフデータには、Graph Query Language(GQL)を使用してクエリを実行できます。Spanner Graph クエリの概要と 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 Graph データの挿入、更新、削除をご覧ください。
例
このセクションでは、グラフデータを作成、更新、削除する方法の例を示します。
グラフデータを挿入する
次の例では、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
}
制限事項
このセクションでは、スキーマレス データ管理の使用に関する制限事項について説明します。
動的ラベルの単一テーブルの要件
定義で動的ラベルを使用する場合、ノードテーブルは 1 つだけにする必要があります。この制限はエッジテーブルにも適用されます。次のことは許可されません。
- 他のノードテーブルと一緒に動的ラベルを持つノードテーブルを定義する。
- 他のエッジテーブルと一緒に動的ラベルを持つエッジテーブルを定義する。
- それぞれ動的ラベルを使用する複数のノードテーブルまたは複数のエッジテーブルを定義する。
たとえば、次のコードは、動的ラベルを持つ複数のグラフノード作成を試みるため失敗します。
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
列として指定します。
2 つのノード間に複数のエッジがある場合は、エッジの一意の 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
を使用してトップレベルのキーにアクセスします。COALESCE
は、JSON_KEYS
が NULL
を返した場合、出力を空の配列に変換し、各キーが小文字であることを確認します。
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);
次の例では、小文字のラベルとプロパティを強制する制約を追加します。最後の 2 つの例では、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 を使用した動的プロパティのバッチ更新が含まれている場合は、パフォーマンスを向上させるために次の推奨事項を検討してください。
行を個別に処理するのではなく、1 つの 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
の 1 階層目のキーが動的プロパティとしてモデル化されているためです。
おすすめの解決策
この動作を回避するには、次の例に示すように、スキーマでプロパティを定義するときに 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 に移行する。