Auf dieser Seite wird beschrieben, wie Sie schemalose Daten in Spanner Graph verwalten. Außerdem finden Sie dort Best Practices und Tipps zur Fehlerbehebung. Grundkenntnisse im Umgang mit dem Spanner Graph-Schema und Abfragen werden empfohlen.
Bei der schemalosen Datenverwaltung können Sie eine flexible Definition eines Graphen erstellen, bei der die Knoten- und Kantentypdefinitionen ohne Schemaänderungen hinzugefügt, aktualisiert oder gelöscht werden können. Der Ansatz unterstützt die iterative Entwicklung und einen geringeren Aufwand für die Schemaverwaltung, während die vertraute Graphabfrage beibehalten wird.
Die schemalose Datenverwaltung ist besonders nützlich für die folgenden Szenarien:
- Sie verwalten Diagramme mit häufigen Änderungen, z. B. Aktualisierungen und Ergänzungen von Elementlabels und -eigenschaften.
- Ihr Graph enthält viele Knoten- und Kantentypen, was das Erstellen und Verwalten von Eingabetabellen schwierig macht.
Schemalose Daten modellieren
Mit Spanner Graph können Sie ein Diagramm aus Tabellen erstellen, in denen Zeilen Knoten und Kanten zugeordnet werden. Anstatt separate Tabellen für jeden Elementtyp zu verwenden, werden bei der schemalosen Datenmodellierung in der Regel eine einzelne Knotentabelle und eine einzelne Kantentabelle mit einer STRING
-Spalte für das Label und einer JSON
-Spalte für Eigenschaften verwendet.
Eingabetabellen erstellen
Sie können eine einzelne GraphNode
-Tabelle und eine einzelne GraphEdge
-Tabelle zum Speichern von schemalosen Daten erstellen, wie im folgenden Beispiel gezeigt. Die Tabellennamen dienen nur zu Veranschaulichung. Sie können eigene Namen verwenden.
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;
Dieses Beispiel tut Folgendes:
Alle Knoten werden in einer einzigen Tabelle
GraphNode
gespeichert, die durch dieid
eindeutig identifiziert wird.Hier werden alle Kanten in einer einzigen Tabelle
GraphEdge
gespeichert, die durch die Kombination aus Quelle (id
), Ziel (dest_id
) und einer eigenen Kennung (edge_id
) eindeutig identifiziert wird.edge_id
ist Teil des Primärschlüssels, um mehr als eine Kante von einemid
-zu-dest_id
-Paar zuzulassen.
Sowohl die Knoten- als auch die Kantentabellen haben eigene label
- und properties
-Spalten vom Typ STRING
bzw. JSON
.
Property-Graph erstellen
Mit der Anweisung CREATE PROPERTY GRAPH werden die Eingabetabellen im vorherigen Abschnitt als Knoten und Kanten zugeordnet. Sie müssen die folgenden Klauseln verwenden, um Labels und Properties für schemalose Daten zu definieren:
DYNAMIC LABEL
: Erstellt das Label eines Knotens oder einer Kante aus einerSTRING
-Spalte der Eingabetabelle.DYNAMIC PROPERTIES
: Erstellt Eigenschaften eines Knotens oder einer Kante aus einerJSON
-Spalte der Eingabetabelle.
Das folgende Beispiel zeigt, wie Sie mithilfe dieser Klauseln einen Graphen erstellen:
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)
);
Dynamisches Label
Mit der DYNAMIC LABEL
-Klausel wird eine Spalte vom Datentyp STRING
zum Speichern der Labelwerte angegeben.
Wenn in einer GraphNode
-Zeile beispielsweise die Spalte label
den Wert person
hat, wird sie einem Person
-Knoten im Diagramm zugeordnet. Wenn in einer GraphEdge
-Zeile die Labelspalte den Wert owns
hat, wird sie ebenfalls einem Owns
-Kante im Graphen zugeordnet.
Dynamische Properties
Mit der DYNAMIC PROPERTIES
-Klausel wird eine Spalte vom Datentyp JSON
zum Speichern von Properties angegeben. JSON-Schlüssel sind die Attributnamen und JSON-Werte sind die Attributwerte.
Wenn die Spalte properties
einer Zeile GraphNode
beispielsweise den JSON-Wert '{"name": "David", "age": 43}'
hat, wird sie einem Knoten mit den Eigenschaften age
und name
mit den Werten 43
und "David"
zugeordnet.
Wann die schemalose Datenverwaltung nicht verwendet werden sollte
In den folgenden Fällen sollten Sie die schemalose Datenverwaltung nicht verwenden:
- Die Knoten- und Kantentypen für Ihre Graphendaten sind klar definiert oder ihre Labels und Properties müssen nicht häufig aktualisiert werden.
- Ihre Daten werden bereits in Spanner gespeichert und Sie möchten lieber Diagramme aus vorhandenen Tabellen erstellen, anstatt neue Knoten- und Kantentabellen zu verwenden.
- Die Einschränkungen von schemalosen Daten verhindern eine Nutzung.
Wenn Ihre Arbeitslast sehr schreibleistungsabhängig ist, insbesondere wenn Properties häufig aktualisiert werden, ist die Verwendung von schemadefinierten Properties mit primitiven Datentypen wie STRING
oder INT64
effektiver als die Verwendung von dynamischen Properties vom Typ JSON
.
Weitere Informationen zum Definieren des Graphschemas ohne dynamische Datenlabels und ‑eigenschaften finden Sie in der Übersicht über Spanner Graph-Schemas.
Schemalose Graphdaten abfragen
Sie können schemalose Graphdaten mit der Graph Query Language (GQL) abfragen. Sie können die Beispielabfragen in der Übersicht über Spanner-Graphabfragen und in der GQL-Referenz mit nur wenigen Änderungen verwenden.
Knoten und Kanten mithilfe von Labels abgleichen
Sie können Knoten und Kanten mit dem Labelausdruck in GQL abgleichen.
Die folgende Abfrage sucht nach verbundenen Knoten und Kanten, die in der Label-Spalte die Werte account
und transfers
haben.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Zugriffseigenschaften
Schlüssel und Werte der obersten Ebene des Datentyps JSON
werden als Eigenschaften modelliert, z. B. age
und name
im folgenden Beispiel.
JSON document |
Properties |
|
|
|
Im folgenden Beispiel wird gezeigt, wie über den Knoten Person
auf die Property name
zugegriffen wird.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
Dies gibt Ergebnisse wie die folgenden zurück:
JSON"Tom"
Attributdatentypen konvertieren
Eigenschaften werden als Werte des JSON-Datentyps behandelt. In einigen Fällen, z. B. bei Vergleichen mit SQL-Typen, müssen sie zuerst in einen SQL-Typ konvertiert werden.
Im folgenden Beispiel werden in der Abfrage die folgenden Datentypkonvertierungen ausgeführt:
- Wandelt die Property
is_blocked
in einen booleschen Typ um, um den Ausdruck zu bewerten. - Konvertiert das Attribut
order_number_str
in einen Stringtyp und vergleicht es mit dem Literalwert"302290001255747"
. - Hier wird die Funktion LAX_INT64 verwendet, um
order_number_str
sicher in eine Ganzzahl als Rückgabetyp umzuwandeln.
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;
Dies gibt Ergebnisse wie die folgenden zurück:
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
In Klauseln wie GROUP BY
und ORDER BY
müssen Sie auch den JSON-Datentyp konvertieren. Im folgenden Beispiel wird die Property city
in einen Stringtyp umgewandelt, damit sie für die Gruppierung verwendet werden kann.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
Tipps zum Konvertieren von JSON-Datentypen in SQL-Datentypen:
- Bei strengen Konvertern wie
INT64
werden strenge Typ- und Wertprüfungen durchgeführt. Dies wird empfohlen, wenn der JSON-Datentyp bekannt und erzwungen ist, z. B. wenn Sie Schemaeinschränkungen verwenden, um den Datentyp der Property zu erzwingen. - Flexible Konvertierungsfunktionen wie
LAX_INT64
konvertieren den Wert nach Möglichkeit sicher und gebenNULL
zurück, wenn die Umwandlung nicht möglich ist. Dies wird empfohlen, wenn keine strenge Prüfung erforderlich ist oder Typen nur schwer durchgesetzt werden können.
Weitere Informationen zur Datenkonvertierung finden Sie in den Tipps zur Fehlerbehebung.
Nach Attributwerten filtern
In Property-Filtern werden die Filterparameter als Werte des Datentyps JSON
behandelt. In der folgenden Abfrage wird is_blocked
beispielsweise als JSON-boolean
und order_number_str
als JSON-string
behandelt.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
Dies gibt Ergebnisse wie die folgenden zurück:
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
Der Filterparameter muss mit dem Property-Typ und dem Property-Wert übereinstimmen. Wenn der Filterparameter order_number_str
beispielsweise eine Ganzzahl ist, wird keine Übereinstimmung gefunden, da es sich bei der Property um ein JSON-string
handelt.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Auf verschachtelte JSON-Properties zugreifen
Verschachtelte JSON-Schlüssel und ‑Werte werden nicht als Properties modelliert. Im folgenden Beispiel werden die JSON-Schlüssel city
, state
und country
nicht als Eigenschaften modelliert, da sie unter location
verschachtelt sind. Sie können jedoch mit einem JSON-Operator für den Feldzugriff oder einem JSON-Skriptoperator darauf zugreifen.
JSON document |
Properties |
|
|
|
|
|
Das folgende Beispiel zeigt, wie Sie mit dem JSON-Feld Zugriffsoperator auf verschachtelte Properties zugreifen.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
Dies gibt Ergebnisse wie die folgenden zurück:
"New York"
Schemalose Daten ändern
Spanner Graph ordnet Daten aus Tabellen Graphknoten und -kanten zu. Wenn Sie Daten in der Eingabetabelle ändern, führt dies direkt zu Mutationen der entsprechenden Graphendaten. Weitere Informationen zur Mutation von Graphdaten finden Sie unter Spanner-Graphdaten einfügen, aktualisieren oder löschen.
Beispiele
In diesem Abschnitt finden Sie Beispiele zum Erstellen, Aktualisieren und Löschen von Diagrammdaten.
Diagrammdaten einfügen
Im folgenden Beispiel wird ein person
-Knoten eingefügt. Label- und Property-Namen müssen in Kleinbuchstaben geschrieben werden.
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
Diagrammdaten aktualisieren
Im folgenden Beispiel wird ein Account
-Knoten aktualisiert und die Funktion JSON_SET
wird verwendet, um das Attribut is_blocked
festzulegen.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
Im folgenden Beispiel wird ein person
-Knoten mit einer neuen Reihe von Properties aktualisiert.
UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;
Im folgenden Beispiel wird die Funktion JSON_REMOVE
verwendet, um die Eigenschaft is_blocked
aus einem Account
-Knoten zu entfernen. Nach der Ausführung bleiben alle anderen vorhandenen Properties unverändert.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
Diagrammdaten löschen
Im folgenden Beispiel wird die Kante Transfers
an Account
-Knoten gelöscht, die in blockierte Konten übertragen wurden.
DELETE FROM GraphEdge
WHERE label = "transfers" and id IN {
GRAPH FinGraph
MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
RETURN a.id
}
Beschränkungen
In diesem Abschnitt werden die Einschränkungen der schemalosen Datenverwaltung aufgeführt.
Anforderung an eine einzelne Tabelle für dynamisches Label
Es kann nur eine Knotentabelle geben, wenn in der Definition ein dynamisches Label verwendet wird. Diese Einschränkung gilt auch für die Kantentabelle. Folgendes ist nicht zulässig:
- Definieren Sie eine Knotentabelle mit einem dynamischen Label neben anderen Knotentabellen.
- Definition einer Kantentabelle mit einem dynamischen Label neben anderen Kantentabellen.
- Mehrere Knotentabellen oder Kantentabellen definieren, die jeweils ein dynamisches Label verwenden.
Der folgende Code schlägt beispielsweise fehl, wenn versucht wird, mehrere Graphknoten mit dynamischen Labels zu erstellen.
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 (
...
);
Labelnamen müssen in Kleinbuchstaben geschrieben werden.
Label-Stringwerte müssen in Kleinbuchstaben gespeichert werden, damit sie abgeglichen werden können. Wir empfehlen, diese Regel entweder im Anwendungscode oder mithilfe von Schemaeinschränkungen durchzusetzen.
Label-Stringwerte müssen in Kleinbuchstaben gespeichert werden, bei der Referenzierung in einer Abfrage wird jedoch nicht zwischen Groß- und Kleinschreibung unterschieden.
Im folgenden Beispiel wird gezeigt, wie Sie Labels in Kleinbuchstaben einfügen:
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
Sie können Labels verwenden, bei denen die Groß- und Kleinschreibung ignoriert wird, um GraphNode
oder GraphEdge
abzugleichen.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
Property-Namen müssen in Kleinbuchstaben geschrieben sein.
Property-Namen müssen in Kleinbuchstaben gespeichert werden. Wir empfehlen, diese Regel entweder im Anwendungscode oder mithilfe von Schemaeinschränkungen durchzusetzen.
Attributnamen müssen in Kleinbuchstaben gespeichert werden, bei der Bezugnahme in Abfragen wird jedoch nicht zwischen Groß- und Kleinschreibung unterschieden.
Im folgenden Beispiel werden die Properties name
und age
in Kleinbuchstaben eingefügt.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
Im Abfragetext wird bei Attributnamen nicht zwischen Groß- und Kleinschreibung unterschieden. Sie können beispielsweise Age
oder age
verwenden, um auf die Property zuzugreifen.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
Sonstige Einschränkungen
- Nur Schlüssel der obersten Ebene des Datentyps
JSON
werden als Properties modelliert. - Attributdatentypen müssen den Spezifikationen für Spanner-JSON-Typen entsprechen.
Best Practices
In diesem Abschnitt werden Best Practices für die Modellierung von schemalosen Daten beschrieben.
Definitionen für Primärschlüssel für Knoten und Kanten
Der Schlüssel eines Knotens muss für alle Knoten des Graphen eindeutig sein. Beispiel: als INT64
- oder String-Spalte UUID
.
Wenn zwischen zwei Knoten mehrere Kanten vorhanden sind, müssen Sie für die Kante eine eindeutige Kennung angeben. Im Schemabeispiel wird eine Spalte für die Anwendungslogik INT64
edge_id
verwendet.
Wenn Sie das Schema für Knoten- und Kantentabellen erstellen, können Sie die Spalte label
optional als Primärschlüsselspalte einbeziehen, wenn der Wert unveränderlich ist. In diesem Fall muss der zusammengesetzte Schlüssel, der aus allen Schlüsselspalten gebildet wird, für alle Knoten oder Kanten eindeutig sein. Mit dieser Methode lässt sich die Leistung von Abfragen verbessern, die nur nach Label gefiltert werden.
Weitere Informationen zur Auswahl des Primärschlüssels finden Sie unter Primärschlüssel auswählen.
Sekundärer Index für eine Property, auf die häufig zugegriffen wird
Wenn Sie die Abfrageleistung für eine Eigenschaft, die häufig in Filtern verwendet wird, verbessern möchten, können Sie einen sekundären Index für eine generierte Property-Spalte erstellen und dann im Graphschema und in Abfragen verwenden.
Im folgenden Beispiel wird der Tabelle GraphNode
für einen person
-Knoten eine generierte Spalte age
hinzugefügt. Bei Knoten ohne das Label person
ist der Wert NULL
.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
Es wird dann eine NULL FILTERED INDEX
für person_age
erstellt und in die Tabelle GraphNode
für den lokalen Zugriff eingefügt.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
Die Tabelle GraphNode
enthält jetzt neue Spalten, die als Knoteneigenschaften für die Grafik verfügbar sind. Verwenden Sie die Anweisung CREATE OR REPLACE PROPERTY GRAPH
, um dies in Ihrer Property-Graph-Definition widerzuspiegeln. Dadurch wird die Definition neu kompiliert und die neue Spalte person_age
als Property eingefügt.
Mit der folgenden Anweisung wird die Definition neu kompiliert und die neue Spalte person_age
als Property eingefügt.
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)
);
Im folgenden Beispiel wird eine Abfrage mit der indexierten Property ausgeführt.
GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;
Optional können Sie nach dem Erstellen des Index den Befehl ANALYZE
ausführen, damit der Abfrageoptimierer mit den neuesten Datenbankstatistiken aktualisiert wird.
Einschränkungen für die Datenintegrität prüfen
Spanner unterstützt Schemaobjekte wie Prüfeinschränkungen, um die Integrität von Label- und Property-Daten durchzusetzen. In diesem Abschnitt finden Sie Empfehlungen für Prüfeinschränkungen, die Sie für schemalose Daten verwenden können.
Gültige Labelwerte erzwingen
Wir empfehlen, in der Definition der Labelspalte NOT NULL
zu verwenden, um nicht definierte Labelwerte zu vermeiden.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
Kleinschreibung für Labelwerte und Property-Namen erzwingen
Da Label- und Property-Namen in Kleinbuchstaben gespeichert werden müssen, empfehlen wir Folgendes:
- Erzwingen Sie die Prüfung in Ihrer Anwendungslogik.
- Erstellen Sie Prüfeinschränkungen im Schema.
Bei Abfragen wird bei Label- und Attributnamen nicht zwischen Groß- und Kleinschreibung unterschieden.
Im folgenden Beispiel wird der Tabelle GraphNode
eine Einschränkung für Knotenlabels hinzugefügt, damit das Label in Kleinbuchstaben geschrieben wird.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
Im folgenden Beispiel wird dem Namen der Edge-Eigenschaft eine Prüfeinschränkung hinzugefügt. Für die Prüfung wird JSON_KEYS
verwendet, um auf die Schlüssel der obersten Ebene zuzugreifen. COALESCE
wandelt die Ausgabe in ein leeres Array um, wenn JSON_KEYS
NULL
zurückgibt, und prüft dann, ob jeder Schlüssel in Kleinbuchstaben geschrieben ist.
ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
Vorhandensein der Property erzwingen
Sie können eine Einschränkung erstellen, die prüft, ob eine Property für ein Label vorhanden ist.
Im folgenden Beispiel wird mit der Einschränkung geprüft, ob ein person
-Knoten ein name
-Attribut hat.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
Eindeutigkeit von Properties erzwingen
Sie können eigenschaftsbasierte Einschränkungen erstellen, mit denen geprüft wird, ob die Eigenschaft eines Knotens oder einer Kante für alle Knoten oder Kanten mit demselben Label eindeutig ist. Verwenden Sie dazu einen UNIQUE INDEX für die generierten Spalten von Properties.
Im folgenden Beispiel wird mit dem eindeutigen Index geprüft, ob die kombinierten Eigenschaften name
und country
für jeden person
-Knoten eindeutig sind.
Fügen Sie eine generierte Spalte für
PersonName
hinzu.ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
Fügen Sie eine generierte Spalte für
PersonCountry
hinzu.ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
Erstellen Sie einen eindeutigen
NULL_FILTERED
-Index für die PropertiesPersonName
undPersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Attributdatentypen erzwingen
Sie können einen Attributdatentyp mithilfe einer Datentypbeschränkung für einen Attributwert für ein Label erzwingen, wie im folgenden Beispiel gezeigt. In diesem Beispiel wird mit der Funktion JSON_TYPE
geprüft, ob für die Property name
des person
-Labels der Typ STRING
verwendet wird.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
Definierte und dynamische Labels kombinieren
Mit Spanner können Knoten in Ihrer Property-Graph-Datei sowohl definierte Labels (im Schema definiert) als auch dynamische Labels (aus Daten abgeleitet) haben. Sie können Labels anpassen, um diese Flexibilität zu nutzen.
Im folgenden Schema wird die Erstellung der Tabelle GraphNode
dargestellt:
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)
);
Hier hat jeder Knoten, der über GraphNode
erstellt wurde, das definierte Label Entity
. Außerdem hat jeder Knoten ein dynamisches Label, das durch den Wert in der Label-Spalte bestimmt wird.
Sie können dann Abfragen schreiben, die Knoten basierend auf dem jeweiligen Labeltyp abgleichen. Mit der folgenden Abfrage werden beispielsweise Knoten mit dem definierten Label Entity
gefunden:
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
Auch wenn in dieser Abfrage das definierte Label Entity
verwendet wird, hat der übereinstimmende Knoten auch ein dynamisches Label, das auf seinen Daten basiert.
Schemabeispiele
Sie können die Schemabeispiele in diesem Abschnitt als Vorlagen verwenden, um Ihre eigenen Schemas zu erstellen. Zu den wichtigsten Schemakomponenten gehören:
- Eingabetabellen für Diagramme erstellen
- Attributgrafik erstellen
- Optional: Index für die Rückwärtstraversale von Kanten, um die Leistung der Rückwärtstraversale zu verbessern
- Optional: Labelindex zur Leistungssteigerung von Abfragen nach Labels
- Optional: Schemaeinschränkungen, um Kleinbuchstaben für Labels und Eigenschaftsnamen zu erzwingen
Im folgenden Beispiel wird gezeigt, wie Sie Eingabetabellen und eine Property-Graphik erstellen:
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)
);
Im folgenden Beispiel wird ein Index verwendet, um die Rückwärtstraversale von Kanten zu verbessern. Die STORING (properties)
-Klausel enthält eine Kopie der Kanteneigenschaften, wodurch Abfragen beschleunigt werden, bei denen nach diesen Eigenschaften gefiltert wird. Sie können die STORING (properties)
-Klausel weglassen, wenn sie für Ihre Abfragen nicht von Vorteil ist.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
Im folgenden Beispiel wird ein Labelindex verwendet, um das Abgleichen von Knoten nach Labels zu beschleunigen.
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
Im folgenden Beispiel werden Einschränkungen hinzugefügt, die Kleinbuchstaben für Labels und Properties erzwingen. In den letzten beiden Beispielen wird die Funktion JSON_KEYS
verwendet. Optional können Sie die Prüfung auf Kleinbuchstaben in der Anwendungslogik erzwingen.
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)));
Batch-Aktualisierungen dynamischer Properties mit DML optimieren
Wenn Sie dynamische Properties mit Funktionen wie JSON_SET
und JSON_REMOVE
ändern, werden Read-Modify-Write-Vorgänge ausgeführt. Sie können im Vergleich zur Aktualisierung von Properties vom Typ STRING
oder INT64
zu höheren Kosten führen.
Wenn Ihre Arbeitslasten Batch-Änderungen an dynamischen Properties mit DML umfassen, können Sie mit den folgenden Empfehlungen die Leistung verbessern:
Aktualisieren Sie mehrere Zeilen in einer einzigen DML-Anweisung, anstatt Zeilen einzeln zu verarbeiten.
Wenn Sie einen Schlüsselbereich mit vielen Zeilen aktualisieren, gruppieren und sortieren Sie die betroffenen Zeilen nach ihren Primärschlüsseln. Wenn Sie nicht überlappende Bereiche mit jeder DML aktualisieren, wird die Sperrungskonfliktrate reduziert.
Verwenden Sie Abfrageparameter in DML-Anweisungen, anstatt sie hartzucodieren, um die Leistung zu verbessern.
Basierend auf diesen Vorschlägen wird im folgenden Beispiel die Property is_blocked
für 100 Knoten in einer einzigen DML-Anweisung aktualisiert. Zu den Abfrageparametern gehören:
@node_ids
: Die Schlüssel vonGraphNode
-Zeilen, die in einemARRAY
-Parameter gespeichert sind. Wenn möglich, sollten sie in DMLs gruppiert und sortiert werden, um eine bessere Leistung zu erzielen.@is_blocked_values
: Die entsprechenden Werte, die aktualisiert werden sollen, werden in einemARRAY
-Parameter gespeichert.
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)
Fehlerbehebung
In diesem Abschnitt wird beschrieben, wie Sie Probleme mit schemalosen Daten beheben.
Property wird im TO_JSON
-Ergebnis mehrmals angezeigt
Problem
Im folgenden Knoten werden die Eigenschaften birthday
und name
als dynamische Eigenschaften in der Spalte JSON
modelliert. Im JSON-Ergebnis des Graphenelements sind doppelte Eigenschaften von birthday
und name
zu sehen.
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
Dies gibt Ergebnisse wie die folgenden zurück:
{
…,
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 14,
"label": "person",
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex"
}
}
…
}
Mögliche Ursache
Standardmäßig werden alle Spalten der Basistabelle als Eigenschaften definiert. Wenn Sie TO_JSON
oder SAFE_TO_JSON
verwenden, um Diagramme zurückzugeben, führt dies zu doppelten Properties. Das liegt daran, dass die Spalte JSON
(properties
) eine schemadefinierte Property ist, während die Schlüssel der ersten Ebene der JSON
als dynamische Properties modelliert werden.
Empfohlene Lösung
Um dieses Verhalten zu vermeiden, schließen Sie die Spalte properties
beim Definieren von Properties im Schema mit der Klausel PROPERTIES ALL COLUMNS EXCEPT
aus, wie im folgenden Beispiel gezeigt:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Nach der Schemaänderung enthalten die zurückgegebenen Graphenelemente des Datentyps JSON
keine Duplikate.
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
Diese Abfrage gibt Folgendes zurück:
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
Häufige Probleme bei der ordnungsgemäßen Umwandlung von Property-Werten
Eine häufige Lösung für die folgenden Probleme besteht darin, immer Property-Wert-Conversions zu verwenden, wenn eine Property in einem Abfrageausdruck verwendet wird.
Vergleich von Property-Werten ohne Conversion
Problem
No matching signature for operator = for argument types: JSON, STRING
Mögliche Ursache
Die Abfrage wandelt die Property-Werte nicht richtig um. Im Vergleich dazu wird das Attribut name
nicht in den Typ STRING
konvertiert:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Empfohlene Lösung
Verwenden Sie vor dem Vergleich eine Wertumwandlung, um dieses Problem zu beheben.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
Dies gibt Ergebnisse wie die folgenden zurück:
+------+
| id |
+------+
| 1 |
+------+
Alternativ können Sie einen Property-Filter verwenden, um Gleichheitsvergleiche zu vereinfachen, bei denen die Wertumwandlung automatisch erfolgt. Der Werttyp („Alex“) muss genau mit dem STRING
-Typ der Property in JSON
übereinstimmen.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
Dies gibt Ergebnisse wie die folgenden zurück:
+------+
| id |
+------+
| 1 |
+------+
RETURN DISTINCT
-Property-Wert ohne Conversion verwenden
Problem
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
Mögliche Ursache
Im folgenden Beispiel wird order_number_str
nicht konvertiert, bevor es in der RETURN DISTINCT
-Anweisung verwendet wird:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
Empfohlene Lösung
Verwenden Sie vor RETURN DISTINCT
eine Wertumwandlung, um dieses Problem zu beheben.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
Dies gibt Ergebnisse wie die folgenden zurück:
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
Property wird als Gruppierungsschlüssel ohne Conversion verwendet
Problem
Grouping by expressions of type JSON is not allowed.
Mögliche Ursache
Im folgenden Beispiel wird t.order_number_str
nicht konvertiert, bevor es zum Gruppieren von JSON-Objekten verwendet wird:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
Empfohlene Lösung
Um dieses Problem zu beheben, sollten Sie eine Wertumwandlung verwenden, bevor Sie die Property als Gruppierungsschlüssel verwenden.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
Dies gibt Ergebnisse wie die folgenden zurück:
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
Attribut, das ohne Umwandlung als Sortierfeld verwendet wird
Problem
ORDER BY does not support expressions of type JSON
Mögliche Ursache
Im folgenden Beispiel wird t.amount
nicht konvertiert, bevor es zum Sortieren von Ergebnissen verwendet wird:
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;
Empfohlene Lösung
Um dieses Problem zu beheben, führen Sie in der ORDER BY
-Klausel eine Conversion für t.amount
durch.
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;
Dies gibt Ergebnisse wie die folgenden zurück:
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
Nicht übereinstimmender Typ bei der Umwandlung
Problem
The provided JSON input is not an integer
Mögliche Ursache
Im folgenden Beispiel wird die Property order_number_str
als JSON-STRING
-Datentyp gespeichert. Wenn Sie versuchen, eine Umwandlung in INT64
vorzunehmen, wird ein Fehler zurückgegeben.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Empfohlene Lösung
Verwenden Sie zum Beheben dieses Problems den genauen Wertkonverter, der dem Werttyp entspricht.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
Dies gibt Ergebnisse wie die folgenden zurück:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Alternativ können Sie einen flexiblen Konverter verwenden, wenn der Wert in den Zieltyp umgewandelt werden kann, wie im folgenden Beispiel gezeigt:
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Dies gibt Ergebnisse wie die folgenden zurück:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Nächste Schritte
- Weitere Informationen zu JSON finden Sie unter JSON-Daten ändern und Liste der JSON-Funktionen.
- Spanner Graph und openCypher vergleichen
- Zu Cloud Spanner Graph migrieren