Schemalose Daten verwalten

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, dass Sie mit dem Schema und den Abfragen von Spanner Graph vertraut sind.

Mit der schemalosen Datenverwaltung können Sie eine flexible Diagrammdefinition erstellen. Sie können Knoten- und Kantentypdefinitionen ohne Schemaänderungen hinzufügen, aktualisieren oder löschen. Dieser Ansatz unterstützt die iterative Entwicklung und reduziert den Aufwand für die Schemaverwaltung, während die vertraute Graphabfrage-Umgebung beibehalten wird.

Die schemalose Datenverwaltung ist in folgenden Szenarien nützlich:

  • Verwalten von Graphen mit häufigen Änderungen, z. B. Aktualisierungen und Ergänzungen von Elementlabels und ‑attributen.

  • Diagramme mit vielen Knoten- und Kantentypen, was die Erstellung und Verwaltung von Eingabetabellen mühsam macht.

Weitere Informationen dazu, wann Sie die schemalose Datenverwaltung verwenden sollten, finden Sie unter Überlegungen zur schemalosen Datenverwaltung.

Schemalose Daten modellieren

Mit Spanner Graph können Sie einen Graphen 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 Attribute verwendet.

Eingabetabellen erstellen

Sie können eine einzelne GraphNode-Tabelle und eine einzelne GraphEdge-Tabelle erstellen, um schemalose Daten zu speichern, wie im folgenden Beispiel gezeigt. Die Tabellennamen dienen nur zur Veranschaulichung. Sie können Ihre eigenen Namen wä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:

  • Speichert alle Knoten in einer einzigen Tabelle, GraphNode, die durch eine eindeutige id identifiziert wird.

  • Speichert alle Kanten in einer einzigen Tabelle, GraphEdge, die durch eine eindeutige Kombination aus Quelle (id), Ziel (dest_id) und eigener Kennung (edge_id) identifiziert wird. Eine edge_id ist Teil des Primärschlüssels, um mehr als eine Kante von einem id- zu einem dest_id-Paar zuzulassen.

Sowohl die Knoten- als auch die Kantentabellen haben eigene label- und properties-Spalten. Diese Spalten sind vom Typ STRING bzw. JSON.

Weitere Informationen zu den Schlüsseloptionen für die schemalose Datenverwaltung finden Sie unter Primärschlüsseldefinitionen für Knoten und Kanten.

Eigenschaftsgrafik 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 einer STRING-Spalte aus der Eingabetabelle.
  • DYNAMIC PROPERTIES: Erstellt Attribute eines Knotens oder einer Kante aus einer JSON-Spalte, die aus der Eingabetabelle stammt.

Das folgende Beispiel zeigt, wie Sie mit diesen 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)
  );

Dynamische Labels definieren

Mit der DYNAMIC LABEL-Klausel wird eine Spalte vom Datentyp STRING zum Speichern der Labelwerte angegeben.

Wenn die Spalte label in einer GraphNode-Zeile beispielsweise den Wert person hat, wird sie einem Person-Knoten im Diagramm zugeordnet. In einer GraphEdge-Zeile wird ein Wert von owns in der Labelspalte einer Owns-Kante im Diagramm zugeordnet.

GraphNode-Label einem GraphEdge-Label zuordnen

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 Zeile GraphNode den JSON-Wert '{"name": "David", "age": 43}' hat, ordnet Spanner sie einem Knoten zu, der die Attribute age und name mit den Werten 43 und "David" hat.

Überlegungen zur schemalosen Datenverwaltung

In den folgenden Szenarien sollten Sie die schemalose Datenverwaltung möglicherweise nicht verwenden:

  • Die Knoten- und Kantentypen für Ihre Grafikdaten sind klar definiert oder ihre Labels und Attribute müssen nicht häufig aktualisiert werden.
  • Ihre Daten sind bereits in Spanner gespeichert und Sie möchten Graphen 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 Schreibleistung 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.

Daten aus schemalosen Graphen 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 geringfügigen Änderungen verwenden.

Knoten und Kanten mithilfe von Labels abgleichen

Sie können Knoten und Kanten mithilfe des Labelausdrucks in GQL abgleichen.

Die folgende Abfrage findet übereinstimmende verbundene Knoten und Kanten, 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;

Zugriffseigenschaften

In Spanner-Modellen werden Schlüssel und Werte der obersten Ebene des Datentyps JSON als Attribute dargestellt, z. B. age und name im folgenden Beispiel.

JSON document Properties

   {
     "name": "Tom",
     "age": 43,
   }
"name": "Tom"
"age": 43

Im folgenden Beispiel wird gezeigt, wie Sie vom Knoten Person auf das Attribut name zugreifen.

GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;

Die Abfrage gibt Ergebnisse ähnlich den folgenden zurück:

"Tom"

Nach dynamischen Attributen filtern, vergleichen und sortieren

Sie können dynamische Attribute direkt in Attributfiltern von Spanner Graph und in den Klauseln WHERE, GROUP BY, ORDER BY und RETURN DISTINCT verwenden. Das liegt daran, dass Cloud Spanner dynamische Attribute als Werte des Datentyps JSON behandelt, der den Vergleich und die Sortierung von JSON-Daten unterstützt.

In der folgenden Abfrage wird beispielsweise in der WHERE-Klausel nach dem booleschen Attribut is_blocked und dem String-Attribut order_number_str gefiltert:

GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE a.is_blocked = false AND t.order_number_str = "302290001255747"
RETURN a.id AS account_id;

Dies gibt Ergebnisse wie die folgenden zurück:

+------------+
| account_id |
+------------+
| 7          |
+------------+

Sie können dieselben Gleichheitsbedingungen mit Attributfiltern für Elementmuster ausdrücken:

GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: "302290001255747"}]->()
RETURN a.id AS account_id;

Sie können dynamische Attribute auch direkt in GROUP BY-, ORDER BY- und RETURN DISTINCT-Klauseln verwenden:

  • GROUP BY-Beispiel:

    GRAPH FinGraph
    MATCH (person:Person {country: "South Korea"})
    RETURN person.city AS person_city, COUNT(*) AS cnt
    LIMIT 10;
    
  • ORDER BY-Beispiel:

    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;
    
  • RETURN DISTINCT-Beispiel:

    GRAPH FinGraph
    MATCH -[t:Transfers]->
    RETURN DISTINCT t.order_number_str AS order_number_str;
    

Beim Filtern, Vergleichen oder Sortieren dynamischer Attribute gelten die folgenden Regeln:

  • Der verglichene Wert muss sowohl mit dem JSON-Typ als auch mit dem Wert des Attributs übereinstimmen. Da order_number_str beispielsweise als JSON-string gespeichert wird, wird beim Vergleich mit einem Ganzzahlliteral keine Übereinstimmung gefunden:

    GRAPH FinGraph
    MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
    RETURN t.order_number_str;
    
  • Wenn Sie eine boolesche JSON-Property direkt als eigenständige WHERE-Bedingung auswerten, z. B. WHERE a.is_blocked, wird ein Fehler zurückgegeben. Vergleichen Sie die Eigenschaft stattdessen mit einem booleschen Literal wie WHERE a.is_blocked = true oder konvertieren Sie sie explizit mit WHERE BOOL(a.is_blocked).

  • Detaillierte Regeln und Einschränkungen zum Vergleichen, Gruppieren und Sortieren von JSON-Werten finden Sie unter JSON-Vergleich und -Sortierung und JSON-Vergleichserzwingung.

Datentypen dynamischer Eigenschaften konvertieren

Dynamische Attribute in den folgenden Szenarien in SQL-Datentypen konvertieren:

  • Konvertieren eines Attributwerts in einen anderen Datentyp für den Vergleich oder die Ausgabe, z. B. Konvertieren des JSON-Strings "302290001255747" in eine INT64-Ganzzahl oder Zurückgeben eines SQL-Datentyps anstelle von JSON.
  • Sie verwenden einen Attributwert in einer SQL-Funktion, einem SQL-Operator oder einem SQL-Ausdruck, der keine JSON-Operanden unterstützt, z. B. arithmetische Operatoren (+, -), Stringfunktionen (LOWER, LIKE), Aggregatfunktionen (SUM, AVG) oder IN UNNEST.
  • Sie definieren eine generierte Spalte für eine Property für einen sekundären Index oder einen eindeutigen Index, da JSON-Ausdrücke nicht direkt indexiert werden können.

Im folgenden Beispiel wird order_number_str als JSON-string gespeichert. Die Abfrage verwendet die Funktion LAX_INT64, um den Wert für den Vergleich und die Rückgabe in eine INT64-Ganzzahl zu konvertieren:

GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE a.is_blocked = true AND LAX_INT64(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       |
+-----------------------+

Auf verschachtelte JSON-Attribute zugreifen

Spanner modelliert verschachtelte JSON-Schlüssel und -Werte nicht als Attribute. Im folgenden Beispiel werden die JSON-Schlüssel city, state und country in Spanner nicht als Attribute modelliert, da sie unter location verschachtelt sind. Sie können jedoch mit einem JSON-Feldzugriffsoperator oder einem JSON-Subscript-Operator darauf zugreifen.

JSON document Properties

   {
     "name": "Tom",
     "age": 43,
     "location": {
       "city": "New York",
       "state": "NY",
       "country": "USA",
     }
   }
"name": "Tom"
"age": 34
"location": {
  "city": "New York",
  "state": "NY",
  "country": "USA",
}

Im folgenden Beispiel wird gezeigt, wie Sie mit dem Zugriffsoperator für JSON-Felder auf verschachtelte Attribute zugreifen.

GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.location.city;

Dies gibt Ergebnisse wie die folgenden zurück:

"New York"

Schemalose Daten ändern

In Spanner Graph werden Daten aus Tabellen Knoten und Kanten im Graphen zugeordnet. Wenn Sie Daten in der Eingabetabelle ändern, führt diese Änderung direkt zu Mutationen der entsprechenden Grafikdaten. Weitere Informationen zur Mutation von Graphendaten finden Sie unter Spanner Graph-Daten einfügen, aktualisieren oder löschen.

Beispielabfragen

In diesem Abschnitt finden Sie Beispiele, die zeigen, wie Sie Diagrammdaten erstellen, aktualisieren und löschen.

Diagrammdaten einfügen

Im folgenden Beispiel wird ein person-Knoten eingefügt. Label- und Attributnamen müssen Kleinbuchstaben enthalten.

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 Attribute 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 Edge Transfers auf Account-Knoten gelöscht, die an 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 der schemalosen Datenverwaltung aufgeführt.

Anforderung einer einzelnen Tabelle für dynamische Labels

Sie können nur eine Knotentabelle haben, wenn in ihrer Definition ein dynamisches Label verwendet wird. 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.
  • Definieren einer Edge-Tabelle mit einem dynamischen Label zusammen mit anderen Edge-Tabellen.
  • Definieren mehrerer Knotentabellen oder mehrerer Kantentabellen, die jeweils ein dynamisches Label verwenden.

Der folgende Code schlägt beispielsweise fehl, wenn versucht wird, mehrere Diagrammknoten 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

Sie müssen Stringwerte für Labels in Kleinbuchstaben speichern, damit sie abgeglichen werden können. Wir empfehlen, diese Regel entweder im Anwendungscode oder mithilfe von Schema-Einschränkungen zu erzwingen.

Labelstringwerte müssen zwar in Kleinbuchstaben gespeichert werden, aber bei der Referenzierung in einer Abfrage wird 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 ohne Berücksichtigung der Groß-/Kleinschreibung verwenden, um die GraphNode oder GraphEdge abzugleichen.

GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;

Attributnamen müssen in Kleinbuchstaben angegeben werden

Attributnamen müssen in Kleinbuchstaben gespeichert werden. Wir empfehlen, diese Regel entweder im Anwendungscode oder mithilfe von Schemabeschränkungen zu erzwingen.

Attributnamen müssen zwar in Kleinbuchstaben gespeichert werden, die Groß- und Kleinschreibung wird jedoch nicht berücksichtigt, wenn Sie in Ihrer Abfrage darauf verweisen.

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

Best Practices für schemalose Daten

In diesem Abschnitt werden Best Practices für die Modellierung schemaloser Daten beschrieben.

Primärschlüssel für Knoten und Kanten definieren

Der Schlüssel eines Knotens sollte für alle Graphknoten eindeutig sein. Beispiel: als INT64- oder Stringspalte UUID.

Wenn zwischen zwei Knoten mehrere Kanten vorhanden sind, führen Sie eine eindeutige Kennung für die Kante ein. Im Schemabeispiel wird die Spalte INT64 edge_id für die Anwendungslogik verwendet.

Wenn Sie das Schema für Knoten- und Edge-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 besteht, für alle Knoten oder Kanten eindeutig sein. Diese Technik verbessert die Leistung bei Abfragen, die nur nach Label gefiltert werden.

Weitere Informationen zur Auswahl des Primärschlüssels finden Sie unter Primärschlüssel auswählen.

Sekundärindex für eine häufig aufgerufene Eigenschaft erstellen

Wenn Sie die Abfrageleistung für ein Attribut verbessern möchten, das häufig in Filtern verwendet wird, erstellen Sie einen sekundären Index für eine generierte Attributspalte. Anschließend können Sie ihn in einem Graphschema 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 verschachtelt.

CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;

Die Tabelle GraphNode enthält neue Spalten, die als Graphknotenattribute 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 aufgenommen.

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 Attribut 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 dem indexierten Attribut 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.

CHECK-Einschränkungen für die Datenintegrität verwenden

Spanner unterstützt Schemaobjekte wie Prüfeinschränkungen, um die Datenintegrität von Labels und Attributen zu erzwingen. In diesem Abschnitt finden Sie Empfehlungen für Prüfeinschrä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);

Kleinbuchstaben für Labelwerte und Attributnamen erzwingen

Da Label- und Attributnamen als Kleinbuchstaben gespeichert werden müssen, haben Sie folgende Möglichkeiten:

Bei der Abfrage wird die Groß-/Kleinschreibung des Labels und des Attributnamens nicht berücksichtigt.

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 geschrieben 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. COALESCE konvertiert die Ausgabe in ein leeres Array, wenn JSON_KEYS den Wert 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 von Attributen erzwingen

Erstellen Sie eine Einschränkung, mit der geprüft wird, ob ein Attribut 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));

Eindeutige Attribute erzwingen

Erstellen Sie attributbasierte Einschränkungen, mit denen geprüft wird, ob das Attribut 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 Attributen.

Im folgenden Beispiel wird mit dem eindeutigen Index geprüft, ob die Attribute name und country zusammen für jeden person-Knoten eindeutig sind.

  1. 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;
    
  2. 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;
    
  3. Erstellen Sie einen eindeutigen NULL_FILTERED-Index für die Attribute PersonName und PersonCountry.

    CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson
    ON GraphNode (person_name, person_country);
    

Datentyp für Attribut erzwingen

Erzwingen Sie einen Attributdatentyp mithilfe einer Datentypeinschränkung für einen Attributwert für ein Label, wie im folgenden Beispiel gezeigt. In diesem Beispiel wird die Funktion JSON_TYPE verwendet, um zu prüfen, ob das Attribut name des Labels person den Typ STRING verwendet.

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.

Das folgende Schema zeigt die Erstellung der Tabelle 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)
  );

Jeder Knoten, der aus GraphNode erstellt wird, hat das definierte Label Entity. Außerdem hat jeder Knoten ein dynamisches Label, das durch den Wert in der Labelspalte bestimmt wird.

Schreiben Sie dann Abfragen, die Knoten anhand eines der beiden Labeltypen 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;

Obwohl diese Abfrage das definierte Label Entity verwendet, hat der übereinstimmende Knoten auch ein dynamisches Label, das auf seinen Daten basiert.

Schemabeispiele

Verwenden Sie die Schemabeispiele in diesem Abschnitt als Vorlagen, um Ihre eigenen Schemas zu erstellen. Zu den wichtigsten Schemakomponenten gehören:

Im folgenden Beispiel wird gezeigt, wie Sie Eingabetabellen und ein Eigenschaftendiagramm 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 das Durchlaufen von Kanten in umgekehrter Richtung zu verbessern. Die STORING (properties)-Klausel enthält eine Kopie der Edge-Attribute, was Abfragen beschleunigt, die nach diesen Attributen filtern. Sie können die STORING (properties)-Klausel weglassen, wenn Ihre Abfragen 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-Updates dynamischer Attribute mit DML optimieren

Das Ändern dynamischer Attribute mit Funktionen wie JSON_SET und JSON_REMOVE umfasst Lese-, Änderungs- und Schreibvorgänge. Dies kann zu höheren Kosten führen als das Aktualisieren von Attributen vom Typ STRING oder INT64.

Wenn Arbeitslasten Batch-Updates für dynamische Attribute 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. Wenn Sie nicht überlappende Bereiche mit jeder DML-Anweisung aktualisieren, wird die Sperrenkonkurrenz reduziert.

  • Verwenden Sie Abfrageparameter in DML-Anweisungen, anstatt sie hartzucodieren, um die Leistung zu verbessern.

Basierend auf diesen Vorschlägen zeigt das folgende Beispiel, wie Sie das Attribut is_blocked für 100 Knoten in einer einzigen DML-Anweisung aktualisieren. Die Abfrageparameter umfassen Folgendes:

  1. @node_ids: Schlüssel von GraphNode Zeilen, die in einem ARRAY-Parameter gespeichert sind. Falls zutreffend, können Sie sie über DMLs hinweg gruppieren und sortieren, um eine bessere Leistung zu erzielen.

  2. @is_blocked_values: Die entsprechenden Werte, die aktualisiert werden sollen, gespeichert in einem ARRAY-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.

Das Attribut kommt mehrmals im TO_JSON-Ergebnis vor

Vorgang

Der folgende Knoten modelliert die Attribute birthday und name als dynamische Attribute in der Spalte JSON. 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 Diagrammelemente zurückzugeben, führt dies zu doppelten Attributen. Das liegt daran, dass die Spalte JSON (properties) ein schemadefiniertes Attribut ist, während die Schlüssel der ersten Ebene von JSON als dynamische Attribute modelliert werden.

Empfohlene Lösung

Um dieses Verhalten zu vermeiden, verwenden Sie die Klausel PROPERTIES ALL COLUMNS EXCEPT, 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",
  }
}

Nächste Schritte