Auf dieser Seite wird beschrieben, wie Sie schemalose Daten in Spanner Graph verwalten. Außerdem finden Sie dort Best Practices und Tipps zur Fehlerbehebung. Wir empfehlen Ihnen, sich mit dem Schema und den Abfragen von Spanner Graph vertraut zu machen.
Mit der schemalosen Datenverwaltung können Sie eine flexible Graphdefinition erstellen. Sie können Knoten- und Kantentypdefinitionen hinzufügen, aktualisieren oder löschen, ohne dass sich das Schema ändert. Dieser Ansatz unterstützt die iterative Entwicklung und reduziert den Aufwand für die Schemabearbeitung, während die vertraute Graphabfrage beibehalten wird.
Die schemalose Datenverwaltung ist in folgenden Szenarien nützlich:
Verwaltung von Diagrammen mit häufigen Änderungen, z. B. Aktualisierungen und Ergänzungen von Elementlabels und ‑attributen.
Graphen mit vielen Knoten- und Kantentypen, was das Erstellen und Verwalten von Eingabetabellen erschwert.
Weitere Informationen dazu, wann die schemalose Datenverwaltung verwendet werden sollte, finden Sie unter Überlegungen zur schemalosen Datenverwaltung.
Daten ohne Schema modellieren
Mit Spanner Graph können Sie einen Graphen aus Tabellen erstellen, in dem Zeilen Knoten und Kanten zugeordnet werden.
Anstatt separate Tabellen für jeden Elementtyp zu verwenden, wird 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 schemaloser Daten erstellen, wie im folgenden Beispiel gezeigt. Die Tabellennamen dienen nur zur Veranschaulichung. Sie können eigene Namen auswählen.
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;
In diesem Beispiel werden die folgenden Aktionen ausgeführt:
Alle Knoten werden in einer einzigen Tabelle,
GraphNode
, gespeichert, die durch eine eindeutigeid
identifiziert wird.Speichert alle Kanten in einer einzigen Tabelle,
GraphEdge
, die durch eine eindeutige Kombination aus Quelle (id
), Ziel (dest_id
) und eigener Kennzeichnung (edge_id
) identifiziert wird. Eineedge_id
ist als Teil des Primärschlüssels enthalten, um mehr als eine Kante von einemid
- zu einemdest_id
-Paar zu ermöglichen.
Sowohl die Knoten- als auch die Kantentabelle haben eigene label
- und properties
-Spalten.
Diese Spalten haben den Typ STRING
bzw. JSON
.
Weitere Informationen zur Auswahl von Schlüsseln für die schemalose Datenverwaltung finden Sie unter Primärschlüsseldefinitionen für Knoten und Kanten.
Property-Graph erstellen
Mit der Anweisung CREATE PROPERTY GRAPH werden die Eingabetabellen im vorherigen Abschnitt als Knoten und Kanten zugeordnet. Verwenden Sie die folgenden Klauseln, um Labels und Attribute für schemalose Daten zu definieren:
DYNAMIC LABEL
: Erstellt das Label eines Knotens oder einer Kante aus einerSTRING
-Spalte der Eingabetabelle.DYNAMIC PROPERTIES
: Erstellt Attribute eines Knotens oder einer Kante aus einerJSON
-Spalte der Eingabetabelle.
Das folgende Beispiel zeigt, wie Sie mit diesen Klauseln ein Diagramm 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)
);
Dynamische Labels definieren
Mit der DYNAMIC LABEL
-Klausel wird eine Spalte mit dem Datentyp STRING
zum Speichern der Labelwerte festgelegt.
Wenn die Spalte label
in einer GraphNode
-Zeile beispielsweise 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 einer Owns
-Kante im Diagramm zugeordnet.
Weitere Informationen zu Einschränkungen bei der Verwendung dynamischer Labels finden Sie unter Einschränkungen.
Dynamische Eigenschaften definieren
Mit der DYNAMIC PROPERTIES
-Klausel wird eine Spalte vom Datentyp JSON
zum Speichern von Attributen angegeben. JSON-Schlüssel stellen Attributnamen und JSON-Werte Attributwerte dar.
Wenn beispielsweise die Spalte properties
einer GraphNode
-Zeile den JSON-Wert '{"name": "David", "age": 43}'
hat, wird sie in Spanner einem Knoten mit den Attributen age
und name
mit den jeweiligen Werten 43
und "David"
zugeordnet.
Hinweise zur schemalosen Datenverwaltung
In den folgenden Szenarien sollten Sie die schemalose Datenverwaltung nicht verwenden:
- Die Knoten- und Kantentypen für Ihre Graphdaten sind gut definiert oder ihre Labels und Eigenschaften müssen nicht häufig aktualisiert werden.
- Ihre Daten sind bereits in Spanner gespeichert und Sie möchten lieber Diagramme aus vorhandenen Tabellen erstellen, anstatt neue, dedizierte Knoten- und Kantentabellen einzuführen.
- Die Einschränkungen von schemalosen Daten verhindern die Einführung.
Wenn Ihr Arbeitslast besonders empfindlich auf die Schreibgeschwindigkeit reagiert, insbesondere wenn Eigenschaften häufig aktualisiert werden, ist die Verwendung von schemadefinierten Eigenschaften mit primitiven Datentypen wie STRING
oder INT64
effektiver als die Verwendung dynamischer Eigenschaften mit dem Typ JSON
.
Weitere Informationen zum Definieren des Graphschemas ohne dynamische Datenlabels und ‑attribute finden Sie in der Übersicht über das Spanner Graph-Schema.
Schemafreie Diagrammdaten abfragen
Sie können schemalose Grafdaten mit Graph Query Language (GQL) abfragen. Sie können die Beispielabfragen in der Übersicht über Spanner Graph-Abfragen und der GQL-Referenz mit nur geringfügigen Änderungen verwenden.
Knoten und Kanten mithilfe von Labels abgleichen
Sie können Knoten und Kanten mithilfe des Label-Ausdrucks in GQL abgleichen.
Die folgende Abfrage stimmt mit verbundenen Knoten und Kanten überein, deren Labelspalte die Werte account
und transfers
enthält.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Auf Eigenschaften zugreifen
In Spanner werden die Schlüssel und Werte der obersten Ebene des Datentyps JSON
als Eigenschaften modelliert, z. B. age
und name
im folgenden Beispiel.
JSON document |
Properties |
|
|
|
Das folgende Beispiel zeigt, wie Sie über den Knoten Person
auf die Property name
zugreifen.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
Die Abfrage gibt Ergebnisse wie die folgenden zurück:
JSON"Tom"
Attributdatentypen konvertieren
In Spanner werden Attribute als Werte des JSON-Datentyps behandelt. In einigen Fällen, z. B. bei Vergleichen mit SQL-Typen, müssen Sie Eigenschaften zuerst in einen SQL-Typ konvertieren.
Im folgenden Beispiel werden in der Abfrage die folgenden Datentypkonvertierungen durchgeführt:
- Wandelt das Attribut
is_blocked
in einen booleschen Typ um, um den Ausdruck auszuwerten. - Konvertiert das Attribut
order_number_str
in einen Stringtyp und vergleicht es mit dem Literalwert"302290001255747"
. - Verwendet die Funktion LAX_INT64, um
order_number_str
sicher in eine Ganzzahl als Rückgabetyp zu konvertieren.
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 konvertiert, sodass Sie sie für die Gruppierung verwenden können.
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:
- Strenge Konverter wie
INT64
führen strenge Typ- und Wertprüfungen durch. Verwenden Sie strenge Konverter, wenn der JSON-Datentyp bekannt ist und erzwungen wird, z. B. durch Verwendung von Schemabeschränkungen, um den Datentyp des Attributs zu erzwingen. - Flexible Konverter wie
LAX_INT64
konvertieren den Wert nach Möglichkeit sicher und gebenNULL
zurück, wenn die Konvertierung nicht möglich ist. Verwenden Sie flexible Konverter, wenn keine strenge Prüfung erforderlich ist oder Typen schwer zu erzwingen sind.
Weitere Informationen zur Datenkonvertierung finden Sie in den Tipps zur Fehlerbehebung.
Nach Attributwerten filtern
In Attributfiltern behandelt Spanner die Filterparameter als Werte des Datentyps JSON
. In der folgenden Abfrage behandelt Spanneris_blocked
als JSON-boolean
und order_number_str
als JSON-string
.
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 dem Property-Typ und -Wert entsprechen. Wenn der Filterparameter order_number_str
beispielsweise eine Ganzzahl ist, findet Spanner keine Übereinstimmung, da die Property ein JSON-string
ist.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Auf verschachtelte JSON-Attribute zugreifen
In Spanner werden verschachtelte JSON-Schlüssel und -Werte nicht als Attribute modelliert.
Im folgenden Beispiel werden die JSON-Schlüssel city
, state
und country
nicht als Attribute modelliert, da sie unter location
verschachtelt sind. Sie können jedoch mit einem JSON-Operator für den Feldzugriff oder einem JSON-Subscript-Operator darauf zugreifen.
JSON document |
Properties |
|
|
|
|
|
Im folgenden Beispiel sehen Sie, wie Sie mit dem JSON-Feldzugriffsoperator auf verschachtelte Attribute 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, werden die entsprechenden Grafdaten direkt mutiert. Weitere Informationen zum Ändern von Graphdaten finden Sie unter Spanner Graph-Daten einfügen, aktualisieren oder löschen.
Beispielabfragen
In diesem Abschnitt finden Sie Beispiele, die zeigen, wie Sie Grafdaten erstellen, aktualisieren und löschen.
Diagrammdaten einfügen
Im folgenden Beispiel wird ein person
-Knoten eingefügt. Für Label- und Attributnamen muss Kleinschreibung verwendet 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
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 Gruppe von Eigenschaften 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 das Attribut is_blocked
aus einem Account
-Knoten zu entfernen. Nach der Ausführung bleiben alle anderen vorhandenen Eigenschaften unverändert.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
Graphdaten löschen
Im folgenden Beispiel wird die Transfers
-Kante für Account
-Knoten gelöscht, die auf gesperrte 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
}
Bekannte Einschränkungen
In diesem Abschnitt werden die Einschränkungen bei der Verwendung der schemalosen Datenverwaltung aufgeführt.
Anforderung für dynamische Labels: nur eine Tabelle
Wenn in der Definition einer Knotentabelle ein dynamisches Label verwendet wird, kann es nur eine Knotentabelle geben. Diese Einschränkung gilt auch für die Edge-Tabelle. In Spanner ist Folgendes nicht zulässig:
- Eine Knotentabelle mit einem dynamischen Label neben anderen Knotentabellen definieren.
- Eine Edge-Tabelle mit einem dynamischen Label neben anderen Edge-Tabellen definieren.
- Sie definieren mehrere Knotentabellen oder mehrere Kantentabellen, die jeweils ein dynamisches Label verwenden.
Der folgende Code schlägt beispielsweise fehl, wenn versucht wird, mehrere Knoten 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-String-Werte müssen für den Abgleich in Kleinbuchstaben gespeichert werden. Wir empfehlen, diese Regel entweder im Anwendungscode oder mithilfe von Schemabeschränkungen zu erzwingen.
Label-Stringwerte müssen zwar in Kleinbuchstaben gespeichert werden, bei einem Verweis in einer Abfrage wird aber 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 nicht beachtet wird, um GraphNode
oder GraphEdge
abzugleichen.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
Eigenschaftsnamen müssen in Kleinbuchstaben geschrieben werden
Sie müssen Attributnamen in Kleinbuchstaben speichern. Wir empfehlen, diese Regel entweder im Anwendungscode oder mithilfe von Schemabeschränkungen zu erzwingen.
Attributnamen müssen zwar in Kleinbuchstaben gespeichert werden, bei der Bezugnahme in der Abfrage wird aber nicht zwischen Groß- und Kleinschreibung unterschieden.
Im folgenden Beispiel werden die Attribute name
und age
in Kleinbuchstaben eingefügt.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
Bei Attributnamen im Abfragetext wird nicht zwischen Groß- und Kleinschreibung unterschieden. Sie können beispielsweise entweder Age
oder age
verwenden, um auf die Property zuzugreifen.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
Zusätzliche Einschränkungen
- Spanner-Modelle verwenden nur Schlüssel der obersten Ebene des Datentyps
JSON
als Attribute. - Die Datentypen von Attributen müssen den Spezifikationen für den Spanner-JSON-Typ entsprechen.
Best Practices für schemalose Daten
In diesem Abschnitt werden Best Practices beschrieben, mit denen Sie schemalose Daten modellieren können.
Primärschlüssel für Knoten und Kanten definieren
Der Schlüssel eines Knotens sollte für alle Knoten im Diagramm eindeutig sein. Beispiel: als INT64
- oder Stringspalte UUID
.
Wenn mehrere Kanten zwischen zwei Knoten vorhanden sind, führen Sie eine eindeutige Kennung für die Kante ein. Im Schema-Beispiel wird die Spalte INT64
edge_id
für die Anwendungslogik verwendet.
Wenn Sie das Schema für Knoten- und Tabellen erstellen, können Sie optional die Spalte label
als Primärschlüsselspalte einfügen, wenn der Wert unveränderlich ist. In diesem Fall sollte der zusammengesetzte Schlüssel, der aus allen Schlüsselspalten gebildet wird, für alle Knoten oder Kanten eindeutig sein. Diese Technik verbessert die Leistung von Abfragen, die nur nach Label gefiltert werden.
Weitere Informationen zur Auswahl des Primärschlüssels finden Sie unter Primärschlüssel auswählen.
Sekundären Index für eine häufig aufgerufene Eigenschaft erstellen
Wenn Sie die Abfrageleistung für eine Property verbessern möchten, die häufig in Filtern verwendet wird, erstellen Sie einen sekundären Index für eine generierte Property-Spalte. Anschließend können Sie es in einem Grafschema und in Abfragen verwenden.
Im folgenden Beispiel wird gezeigt, wie Sie der Tabelle GraphNode
für einen person
-Knoten eine generierte age
-Spalte hinzufügen. Der Wert ist NULL
für Knoten ohne das Label person
.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
Mit der folgenden DDL-Anweisung wird dann ein NULL FILTERED INDEX
für person_age
erstellt und für den lokalen Zugriff in die Tabelle GraphNode
eingefügt.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
Die Tabelle GraphNode
enthält neue Spalten, die als Eigenschaften von Grafikknoten verfügbar sind. Um dies in der Definition des Property-Graphen zu berücksichtigen, verwenden Sie die CREATE OR
REPLACE PROPERTY GRAPH
-Anweisung. Dadurch wird die Definition neu kompiliert und die neue Spalte person_age
als Attribut eingefügt.
Weitere Informationen finden Sie unter Vorhandene Knoten- oder Kantendefinitionen aktualisieren.
Mit der folgenden Anweisung wird die Definition neu kompiliert und die neue Spalte person_age
als Eigenschaft 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 den ANALYZE
-Befehl nach dem Erstellen des Index ausführen, damit der Abfrageoptimierer mit den neuesten Datenbankstatistiken aktualisiert wird.
CHECK-Einschränkungen für die Datenintegrität verwenden
Spanner unterstützt Schemaobjekte wie CHECK-Einschränkungen, um die Datenintegrität von Labels und Attributen zu erzwingen. In diesem Abschnitt finden Sie Empfehlungen für Check-Einschränkungen, die Sie mit schemalosen Daten verwenden können.
Labelwerte erzwingen
Wir empfehlen, NOT NULL
in der Definition der Labelspalte zu verwenden, um undefinierte 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 Attributnamen erzwingen
Da Label- und Attributnamen als Kleinbuchstaben gespeichert werden müssen, haben Sie folgende Möglichkeiten:
- Die Prüfung in der Anwendungslogik erzwingen.
- Erstellen Sie Diagnoseeinschränkungen im Schema.
Bei der Abfrage wird bei Label- und Attributnamen nicht zwischen Groß- und Kleinschreibung unterschieden.
Im folgenden Beispiel wird gezeigt, wie Sie der Tabelle GraphNode
eine Einschränkung für Knotenlabels hinzufügen, um sicherzustellen, dass das Label in Kleinbuchstaben ist.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
Das folgende Beispiel zeigt, wie Sie der Edge-Eigenschaft „name“ eine Check-Einschränkung hinzufügen. Bei der Prüfung wird JSON_KEYS
verwendet, um auf die Schlüssel der obersten Ebene zuzugreifen.
Mit COALESCE
wird die Ausgabe in ein leeres Array konvertiert, wenn JSON_KEYS
NULL
zurückgibt. Anschließend wird geprüft, 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 von Attributen erzwingen
Erstellen Sie eine Einschränkung, mit der geprüft wird, ob für ein Label eine Eigenschaft vorhanden ist.
Im folgenden Beispiel wird geprüft, ob ein person
-Knoten das Attribut name
hat.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
Eindeutige Properties erzwingen
Erstellen Sie eigenschaftsbasierte Einschränkungen, mit denen geprüft wird, ob die Eigenschaft eines Knotens oder einer Kante für 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 Attribute name
und country
zusammen 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
NULL_FILTERED
-Index für die AttributePersonName
undPersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Datentyp des Attributs erzwingen
Sie können einen Attributdatentyp erzwingen, indem Sie eine Datentypeinschränkung für einen Attributwert für ein Label festlegen, wie im folgenden Beispiel gezeigt. In diesem Beispiel wird mit der Funktion JSON_TYPE
geprüft, ob für das Attribut name
des Labels person
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
In Spanner können Knoten im Attributdiagramm sowohl definierte Labels (im Schema definiert) als auch dynamische Labels (aus Daten abgeleitet) haben. Passen Sie die Labels an, um diese Flexibilität zu nutzen.
Sehen Sie sich das folgende Schema an, in dem die Erstellung der Tabelle GraphNode
dargestellt wird:
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)
);
Jeder Knoten, der aus GraphNode
erstellt wird, hat das Label Entity
mit der definierten Eigenschaft. Außerdem hat jeder Knoten ein dynamisches Label, das durch den Wert in der Spalte „Label“ bestimmt wird.
Anschließend schreiben Sie Abfragen, die Knoten basierend auf dem Labeltyp abgleichen. Mit der folgenden Abfrage werden beispielsweise Knoten mit dem definierten Label Entity
gesucht:
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 abgeglichene Knoten auch ein dynamisches Label, das auf seinen Daten basiert.
Schemabeispiele
Verwenden Sie die Schema-Beispiele in diesem Abschnitt als Vorlagen, um Ihre eigenen Schemas zu erstellen. Zu den wichtigsten Schemakomponenten gehören:
- Eingabetabellen für Diagramme erstellen
- Attributgrafik erstellen
- Optional: Index für das Durchlaufen von Kanten in umgekehrter Richtung, um die Leistung beim Durchlaufen in umgekehrter Richtung zu steigern
- Optional: Labelindex, um die Leistung von Abfragen nach Labels zu steigern
- Optional: Schemabeschränkungen, um Labels in Kleinbuchstaben und Eigenschaftennamen zu erzwingen
Im folgenden Beispiel wird gezeigt, wie Eingabetabellen und ein Property Graph erstellt werden:
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 das Durchlaufen von Kanten in umgekehrter Richtung zu optimieren. Die STORING (properties)
-Klausel enthält eine Kopie der Edge-Eigenschaften, was Abfragen beschleunigt, die nach diesen Eigenschaften filtern. Sie können die STORING (properties)
-Klausel weglassen, wenn Ihre Anfragen nicht davon profitieren.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
Im folgenden Beispiel wird ein Labelindex verwendet, um den Abgleich 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 Eigenschaften 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 Eigenschaften mit DML optimieren
Das Ändern dynamischer Eigenschaften mit Funktionen wie JSON_SET
und JSON_REMOVE
umfasst Read-Modify-Write-Vorgänge. Das kann zu höheren Kosten führen als bei der Aktualisierung von Eigenschaften des Typs STRING
oder INT64
.
Wenn Arbeitslasten Batch-Aktualisierungen dynamischer Eigenschaften mit DML umfassen, sollten Sie die folgenden Empfehlungen beachten, um eine bessere Leistung zu erzielen:
Aktualisieren Sie mehrere Zeilen in einer einzigen DML-Anweisung, anstatt Zeilen einzeln zu verarbeiten.
Wenn Sie einen großen Schlüsselbereich aktualisieren, gruppieren und sortieren Sie die betroffenen Zeilen nach Primärschlüsseln. Durch das Aktualisieren nicht überlappender Bereiche mit jeder DML wird die Sperrenkonkurrenz reduziert.
Verwenden Sie Abfrageparameter in DML-Anweisungen anstelle von hartcodierten Werten, um die Leistung zu verbessern.
Anhand dieser Vorschläge wird im folgenden Beispiel gezeigt, wie das Attribut is_blocked
für 100 Knoten in einer einzelnen DML-Anweisung aktualisiert wird. Die Abfrageparameter umfassen Folgendes:
@node_ids
: Schlüssel vonGraphNode
-Zeilen, die in einemARRAY
-Parameter gespeichert sind. Falls zutreffend, wird durch das Gruppieren und Sortieren über DMLs hinweg eine bessere Leistung erzielt.@is_blocked_values
: Die entsprechenden Werte, die aktualisiert werden sollen, gespeichert in einemARRAY
-Parameter.
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 mehrmals im TO_JSON
-Ergebnis angezeigt
Problem
Im folgenden Knoten werden die Attribute birthday
und name
als dynamische Attribute in der Spalte JSON
modelliert. Doppelte birthday
- und name
-Attribute werden im JSON-Ergebnis des Grafikelements angezeigt.
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 Attribute definiert. Wenn Sie TO_JSON
oder SAFE_TO_JSON
verwenden, um Grafikelemente zurückzugeben, werden Eigenschaften dupliziert. Das liegt daran, dass die Spalte JSON
(properties
) eine schemadefinierte Property ist, während die Schlüssel der ersten Ebene von JSON
als dynamische Properties modelliert werden.
Empfohlene Lösung
Um dieses Verhalten zu vermeiden, verwenden Sie die PROPERTIES ALL COLUMNS EXCEPT
-Klausel, um die Spalte properties
auszuschließen, wenn Sie Attribute im Schema definieren, 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 Grafikelemente des Datentyps JSON
keine Duplikate mehr.
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, wenn Attributwerte nicht richtig konvertiert werden
Um die folgenden Probleme zu beheben, verwenden Sie immer Attributwertkonvertierungen, wenn Sie ein Attribut in einem Abfrageausdruck verwenden.
Property-Werte ohne Conversion vergleichen
Problem
No matching signature for operator = for argument types: JSON, STRING
Mögliche Ursache
Die Abfrage konvertiert Attributwerte nicht richtig. Das Attribut name
wird beispielsweise nicht in den Typ STRING
konvertiert:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Empfohlene Lösung
Um dieses Problem zu beheben, sollten Sie vor dem Vergleich eine Wertkonvertierung durchführen.
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 Wertkonvertierung automatisch erfolgt. Der Typ des Werts („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
Problem
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
Mögliche Ursache
Im folgenden Beispiel wurde 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 eine Wertkonvertierung vor RETURN DISTINCT
, 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 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, verwenden Sie eine Wertkonvertierung, 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 als Sortierschlüssel ohne Conversion 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 eine Konvertierung für t.amount
in der ORDER BY
-Klausel 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 |
+--------------+------------+--------+
Typkonflikt bei der Konvertierung
Problem
The provided JSON input is not an integer
Mögliche Ursache
Im folgenden Beispiel wird das Attribut order_number_str
als JSON-Datentyp STRING
gespeichert. Wenn Sie versuchen, eine Konvertierung in INT64
durchzuführen, 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 den Konverter für genaue Werte, der dem Werttyp entspricht, um dieses Problem zu beheben.
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 Converter verwenden, wenn der Wert in den Zieltyp konvertiert 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 Spanner Graph migrieren