Gestire i dati senza schema

Questa pagina spiega come gestire i dati senza schema in Spanner Graph. Fornisce inoltre best practice e suggerimenti per la risoluzione dei problemi. Ti consigliamo di acquisire familiarità con lo schema e le query di Spanner Graph.

La gestione dei dati senza schema consente di creare una definizione di grafo flessibile. Puoi aggiungere, aggiornare o eliminare le definizioni dei tipi di nodi e archi senza modifiche allo schema. Questo approccio supporta lo sviluppo iterativo e riduce l'overhead di gestione dello schema, preservando al contempo l'esperienza di query dei grafi familiare.

La gestione dei dati senza schema è utile per i seguenti scenari:

  • Gestione di grafici con modifiche frequenti, come aggiornamenti e aggiunte di etichette e proprietà degli elementi.

  • Grafi con molti tipi di nodi e archi, il che rende complessa la creazione e la gestione delle tabelle di input.

Per saperne di più su quando utilizzare la gestione dei dati senza schema, consulta Considerazioni sulla gestione dei dati senza schema.

Modellare dati senza schema

Spanner Graph consente di creare un grafo da tabelle che mappano le righe a nodi e archi. Anziché utilizzare tabelle separate per ogni tipo di elemento, la modellazione dei dati senza schema in genere utilizza una singola tabella dei nodi e una singola tabella degli archi con una colonna STRING per l'etichetta e una colonna JSON per le proprietà.

Crea tabelle di input

Puoi creare una singola tabella GraphNode e una singola tabella GraphEdge per archiviare i dati senza schema, come mostrato nell'esempio seguente. I nomi delle tabelle sono a scopo illustrativo, puoi scegliere i tuoi.

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;

Questo esempio esegue le seguenti azioni:

  • Archivia tutti i nodi in una singola tabella, GraphNode, identificata da un id univoco.

  • Memorizza tutti gli archi in una singola tabella, GraphEdge, identificata da una combinazione univoca di origine (id), destinazione (dest_id) e il proprio identificatore (edge_id). Un edge_id è incluso come parte della chiave primaria per consentire più di un arco da una coppia id a dest_id.

Sia la tabella dei nodi sia quella degli archi hanno le proprie colonne label e properties. Queste colonne sono rispettivamente di tipo STRING e JSON.

Per saperne di più sulle scelte delle chiavi per la gestione dei dati senza schema, consulta Definizioni delle chiavi primarie per nodi e archi.

Crea un grafico delle proprietà

L'istruzione CREATE PROPERTY GRAPH mappa le tabelle di input nella sezione precedente come nodi e archi. Utilizza le seguenti clausole per definire etichette e proprietà per i dati senza schema:

  • DYNAMIC LABEL: crea l'etichetta di un nodo o di un arco da una colonna STRING della tabella di input.
  • DYNAMIC PROPERTIES: crea le proprietà di un nodo o di un arco da una colonna JSON della tabella di input.

L'esempio seguente mostra come creare un grafico utilizzando queste clausole:

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)
  );

Definisci le etichette dinamiche

La clausola DYNAMIC LABEL designa una colonna tipizzata STRING per archiviare i valori delle etichette.

Ad esempio, in una riga GraphNode, se la colonna label ha un valore person, viene mappata a un nodo Person all'interno del grafico. Analogamente, in una riga GraphEdge, se la colonna dell'etichetta ha un valore owns, viene mappata a un arco Owns all'interno del grafico.

Mappare un'etichetta GraphNode a un'etichetta GraphEdge

Per saperne di più sulle limitazioni quando utilizzi le etichette dinamiche, vedi Limitazioni.

Definisci le proprietà dinamiche

La clausola DYNAMIC PROPERTIES designa una colonna tipizzata JSON per archiviare le proprietà. Le chiavi JSON rappresentano i nomi delle proprietà e i valori JSON rappresentano i valori delle proprietà.

Ad esempio, quando la colonna properties di una riga GraphNode ha il valore JSON '{"name": "David", "age": 43}', Spanner lo mappa a un nodo che ha proprietà age e name con 43 e "David" come rispettivi valori.

Considerazioni sulla gestione dei dati senza schema

Potresti non voler utilizzare la gestione dei dati senza schema nei seguenti scenari:

  • I tipi di nodi e archi per i dati del grafico sono ben definiti o le relative etichette e proprietà non richiedono aggiornamenti frequenti.
  • I tuoi dati sono già archiviati in Spanner e preferisci creare grafici da tabelle esistenti anziché introdurre nuove tabelle dedicate per nodi e archi.
  • I limiti dei dati senza schema ne impediscono l'adozione.

Inoltre, se il tuo carico di lavoro è molto sensibile alle prestazioni di scrittura, soprattutto quando le proprietà vengono aggiornate di frequente, l'utilizzo di proprietà definite dallo schema con tipi di dati primitivi come STRING o INT64 è più efficace dell'utilizzo di proprietà dinamiche con il tipo JSON.

Per saperne di più su come definire lo schema del grafico senza utilizzare etichette e proprietà dei dati dinamici, consulta la panoramica dello schema di Spanner Graph.

Esegui query sui dati del grafico senza schema

Puoi eseguire query sui dati dei grafi senza schema utilizzando Graph Query Language (GQL). Puoi utilizzare le query di esempio nella panoramica delle query Spanner Graph e nel riferimento GQL con modifiche limitate.

Trova nodi e archi corrispondenti utilizzando le etichette

Puoi trovare nodi e archi corrispondenti utilizzando l'espressione di etichetta in GQL.

La seguente query corrisponde ai nodi e agli archi connessi che hanno i valori account e transfers nella colonna dell'etichetta.

GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;

Accedi alle proprietà

Spanner modella le chiavi e i valori di primo livello del tipo di dati JSON come proprietà, ad esempio age e name nell'esempio seguente.

JSON document Properties

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

L'esempio seguente mostra come accedere alla proprietà name dal nodo Person.

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

La query restituisce risultati simili ai seguenti:

"Tom"

Filtra, confronta e ordina in base alle proprietà dinamiche

Puoi utilizzare le proprietà dinamiche direttamente nei filtri delle proprietà di Spanner Graph e nelle clausole WHERE, GROUP BY, ORDER BY e RETURN DISTINCT. Questo perché Spanner tratta le proprietà dinamiche come valori del tipo di dati JSON, che supporta il confronto e l'ordinamento JSON.

Ad esempio, la seguente query filtra in base alla proprietà booleana is_blocked e alla proprietà stringa order_number_str nella clausola WHERE:

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

Vengono restituiti risultati simili ai seguenti:

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

Puoi esprimere le stesse condizioni di uguaglianza utilizzando i filtri delle proprietà del pattern degli elementi:

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

Puoi anche utilizzare le proprietà dinamiche direttamente nelle clausole GROUP BY, ORDER BY e RETURN DISTINCT:

  • Esempio di GROUP BY:

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

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

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

Quando filtri, confronti o ordini le proprietà dinamiche, si applicano le seguenti regole:

  • Il valore confrontato deve corrispondere sia al tipo JSON sia al valore della proprietà. Ad esempio, poiché order_number_str viene archiviato come string JSON, il confronto con un valore letterale intero non trova corrispondenze:

    GRAPH FinGraph
    MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
    RETURN t.order_number_str;
    
  • La valutazione di una proprietà booleana JSON direttamente come condizione WHERE autonoma, ad esempio WHERE a.is_blocked, restituisce un errore. Confronta invece la proprietà con un valore letterale booleano, ad esempio WHERE a.is_blocked = true, o convertila esplicitamente con WHERE BOOL(a.is_blocked).

  • Per regole e limitazioni dettagliate sul confronto, sul raggruppamento e sull'ordinamento dei valori di JSON, consulta Confronto e ordinamento JSON e Forzatura del confronto JSON.

Converti i tipi di dati delle proprietà dinamiche

Converti le proprietà dinamiche in tipi di dati SQL nei seguenti scenari:

  • Conversione di un valore di proprietà in un tipo di dati diverso per il confronto o l'output, ad esempio la conversione della stringa JSON "302290001255747" in un numero intero INT64 o la restituzione di un tipo di dati SQL anziché JSON.
  • Utilizzo di un valore della proprietà in una funzione, un operatore o un'espressione SQL che non supporta gli operandi JSON, ad esempio operatori aritmetici (+, -), funzioni stringa (LOWER, LIKE), funzioni di aggregazione (SUM, AVG) o IN UNNEST.
  • Definizione di una colonna generata su una proprietà per un indice secondario o un indice univoco, perché le espressioni JSON non possono essere indicizzate direttamente.

Nell'esempio seguente, order_number_str viene memorizzato come string JSON e la query utilizza la funzione LAX_INT64 per convertirlo in un numero intero INT64 per il confronto e la restituzione:

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;

Vengono restituiti risultati simili ai seguenti:

+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747       |
+-----------------------+

Accedere alle proprietà JSON nidificate

Spanner non modella chiavi e valori JSON nidificati come proprietà. Nell'esempio seguente, Spanner non modella le chiavi JSON city, state e country come proprietà perché sono nidificate in location. Tuttavia, puoi accedervi con un operatore di accesso ai campi JSON o un operatore di indice JSON.

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",
}

L'esempio seguente mostra come accedere alle proprietà nidificate con l'operatore di accesso del campo JSON.

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

Vengono restituiti risultati simili ai seguenti:

"New York"

Modifica i dati senza schema

Spanner Graph mappa i dati delle tabelle ai nodi e agli archi del grafico. Quando modifichi i dati della tabella di input, questa modifica causa direttamente mutazioni ai dati del grafico corrispondenti. Per ulteriori informazioni sulla mutazione dei dati del grafo, consulta Inserisci, aggiorna o elimina i dati di Spanner Graph.

Esempi di query

Questa sezione fornisce esempi che mostrano come creare, aggiornare ed eliminare i dati del grafico.

Inserisci i dati del grafico

L'esempio seguente inserisce un nodo person. I nomi delle etichette e delle proprietà devono utilizzare lettere minuscole.

INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');

Aggiornare i dati del grafico

L'esempio seguente aggiorna un nodo Account e utilizza la funzione JSON_SET per impostare la relativa proprietà is_blocked.

UPDATE GraphNode
SET properties = JSON_SET(
  properties,
  '$.is_blocked', false
)
WHERE label = "account" AND id = 16;

L'esempio seguente aggiorna un nodo person con un nuovo insieme di proprietà.

UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;

L'esempio seguente utilizza la funzione JSON_REMOVE per rimuovere la proprietà is_blocked da un nodo Account. Dopo l'esecuzione, tutte le altre proprietà esistenti rimangono invariate.

UPDATE GraphNode
SET properties = JSON_REMOVE(
  properties,
  '$.is_blocked'
)
WHERE label = "account" AND id = 16;

Elimina i dati del grafico

L'esempio seguente elimina l'arco Transfers sui nodi Account che sono stati trasferiti ad account bloccati.

DELETE FROM GraphEdge
WHERE label = "transfers" AND id IN {
  GRAPH FinGraph
  MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
  RETURN a.id
}

Limitazioni note

Questa sezione elenca le limitazioni relative all'utilizzo della gestione dei dati senza schema.

Requisito di una singola tabella per le etichette dinamiche

Puoi avere una sola tabella dei nodi se nella sua definizione viene utilizzata un'etichetta dinamica. Questa limitazione si applica anche alla tabella edge. Spanner non consente quanto segue:

  • Definizione di una tabella dei nodi con un'etichetta dinamica insieme a qualsiasi altra tabella dei nodi.
  • Definizione di una tabella degli archi con un'etichetta dinamica insieme a qualsiasi altra tabella degli archi.
  • Definizione di più tabelle di nodi o più tabelle di archi che utilizzano ciascuna un'etichetta dinamica.

Ad esempio, il seguente codice non va a buon fine quando tenta di creare più nodi del grafico con etichette dinamiche.

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 (
    ...
  );

I nomi delle etichette devono essere in minuscolo

Per la corrispondenza, devi memorizzare i valori delle stringhe delle etichette in minuscolo. Ti consigliamo di applicare questa regola nel codice dell'applicazione o utilizzando i vincoli dello schema.

Anche se i valori delle stringhe delle etichette devono essere memorizzati in minuscolo, non sono sensibili alle maiuscole quando li utilizzi in una query.

Il seguente esempio mostra come inserire etichette con valori minuscoli:

INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");

Puoi utilizzare etichette senza distinzione tra maiuscole e minuscole per trovare corrispondenze con GraphNode o GraphEdge.

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

I nomi delle proprietà devono essere in minuscolo

Devi memorizzare i nomi delle proprietà in lettere minuscole. Ti consigliamo di applicare questa regola nel codice dell'applicazione o utilizzando i vincoli dello schema.

Anche se i nomi delle proprietà devono essere memorizzati in minuscolo, non fanno distinzione tra maiuscole e minuscole quando li utilizzi nella query.

L'esempio seguente inserisce le proprietà name e age utilizzando lettere minuscole.

INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');

Nel testo della query, i nomi delle proprietà non fanno distinzione tra maiuscole e minuscole. Ad esempio, puoi utilizzare Age o age per accedere alla proprietà.

GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;

Ulteriori limitazioni

  • Spanner modella solo le chiavi di primo livello del tipo di dati JSON come proprietà.
  • I tipi di dati delle proprietà devono essere conformi alle specifiche del tipo JSON di Spanner.

Best practice per i dati senza schema

Questa sezione descrive le best practice che ti aiutano a modellare i dati senza schema.

Definisci le chiavi primarie per nodi e archi

La chiave di un nodo deve essere univoca per tutti i nodi del grafo. Ad esempio, come colonna INT64 o stringa UUID.

Se esistono più archi tra due nodi, introduci un identificatore univoco per l'arco. L'esempio di schema utilizza una colonna INT64 edge_id della logica dell'applicazione.

Quando crei lo schema per le tabelle dei nodi e degli archi, includi facoltativamente la colonna label come colonna della chiave primaria se il valore è immutabile. Se lo fai, la chiave composita formata da tutte le colonne chiave deve essere univoca in tutti i nodi o gli archi. Questa tecnica migliora le prestazioni per le query filtrate solo per etichetta.

Per saperne di più sulla scelta della chiave primaria, consulta Scegli una chiave primaria.

Crea un indice secondario per una proprietà a cui si accede di frequente

Per migliorare il rendimento delle query per una proprietà utilizzata di frequente nei filtri, crea un indice secondario in base a una colonna della proprietà generata. Poi, utilizzalo in uno schema e in query di un grafo.

L'esempio seguente mostra come aggiungere una colonna age generata alla tabella GraphNode per un nodo person. Il valore è NULL per i nodi senza l'etichetta person.

ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));

La seguente istruzione DDL crea quindi un NULL FILTERED INDEX per person_age e lo interleave nella tabella GraphNode per l'accesso locale.

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

La tabella GraphNode include nuove colonne disponibili come proprietà dei nodi del grafico. Per riflettere questo aspetto nella definizione del grafico delle proprietà, utilizza l'istruzione CREATE OR REPLACE PROPERTY GRAPH. In questo modo, la definizione viene ricompilata e la nuova colonna person_age viene inclusa come proprietà.

Per ulteriori informazioni, consulta Aggiornamento delle definizioni di nodi o archi esistenti.

La seguente istruzione ricompila la definizione e include la nuova colonna person_age come proprietà.

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)
  );

L'esempio seguente esegue una query con la proprietà indicizzata.

GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;

Facoltativamente, esegui il comando ANALYZE dopo la creazione dell'indice in modo che l'ottimizzatore delle query venga aggiornato con le statistiche più recenti del database.

Utilizzare i vincoli di controllo per l'integrità dei dati

Spanner supporta oggetti schema come i vincoli di controllo per applicare l'integrità dei dati di etichette e proprietà. Questa sezione elenca i consigli per i vincoli di controllo che puoi utilizzare con i dati senza schema.

Applica valori etichetta

Ti consigliamo di utilizzare NOT NULL nella definizione della colonna delle etichette per evitare valori di etichetta non definiti.

CREATE TABLE GraphNode (
  id INT64 NOT NULL,
  label STRING(MAX) NOT NULL,
  properties JSON,
) PRIMARY KEY (id);

Applica valori etichetta e nomi proprietà in minuscolo

Poiché i nomi delle etichette e delle proprietà devono essere memorizzati come valori in minuscolo, esegui una delle seguenti operazioni:

Al momento della query, il nome dell'etichetta e della proprietà non fanno distinzione tra maiuscole e minuscole.

L'esempio seguente mostra come aggiungere un vincolo di etichetta del nodo alla tabella GraphNode per assicurarti che l'etichetta sia in minuscolo.

ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);

L'esempio seguente mostra come aggiungere un vincolo di controllo al nome della proprietà edge. Il controllo utilizza JSON_KEYS per accedere alle chiavi di primo livello. COALESCE converte l'output in un array vuoto se JSON_KEYS restituisce NULL e poi verifica che ogni chiave sia in minuscolo.

ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));

Imponi l'esistenza delle proprietà

Crea un vincolo che controlli se esiste una proprietà per un'etichetta.

Nell'esempio seguente, il vincolo verifica se un nodo person ha una proprietà name.

ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));

Applica proprietà uniche

Crea vincoli basati sulle proprietà che controllano se la proprietà di un nodo o di un arco è univoca tra i nodi o gli archi con la stessa etichetta. Per farlo, utilizza un UNIQUE INDEX per le colonne generate delle proprietà.

Nell'esempio seguente, l'indice univoco verifica che le proprietà name e country combinate siano univoche per qualsiasi nodo person.

  1. Aggiungi una colonna generata per PersonName.

    ALTER TABLE GraphNode
    ADD COLUMN person_name STRING(MAX)
    AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
    
  2. Aggiungi una colonna generata per PersonCountry.

    ALTER TABLE GraphNode
    ADD COLUMN person_country STRING(MAX)
    AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
    
  3. Crea un indice univoco NULL_FILTERED per le proprietà PersonName e PersonCountry.

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

Forza il tipo di dati della proprietà

Applica un tipo di dati della proprietà utilizzando un vincolo del tipo di dati su un valore della proprietà per un'etichetta, come mostrato nell'esempio seguente. Questo esempio utilizza la funzione JSON_TYPE per verificare che la proprietà name dell'etichetta person utilizzi il tipo STRING.

ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));

Combina etichette definite e dinamiche

Spanner consente ai nodi del grafico delle proprietà di avere sia etichette definite (definite nello schema) sia etichette dinamiche (derivate dai dati). Personalizza le etichette per sfruttare questa flessibilità.

Considera lo schema seguente che mostra la creazione della tabella 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)
  );

In questo caso, ogni nodo creato da GraphNode ha l'etichetta Entity definita. Inoltre, ogni nodo ha un'etichetta dinamica determinata dal valore nella colonna delle etichette.

Poi, scrivi query che corrispondano ai nodi in base al tipo di etichetta. Ad esempio, la seguente query trova i nodi utilizzando l'etichetta Entity definita:

GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;

Anche se questa query utilizza l'etichetta definita Entity, ricorda che il nodo corrispondente contiene anche un'etichetta dinamica basata sui suoi dati.

Esempi di schema

Utilizza gli esempi di schema in questa sezione come modelli per creare i tuoi schemi. I componenti chiave dello schema includono:

L'esempio seguente mostra come creare tabelle di input e un grafico delle proprietà:

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)
  );

L'esempio seguente utilizza un indice per migliorare l'attraversamento degli archi inversi. La clausola STORING (properties) include una copia delle proprietà dei bordi, il che velocizza le query che filtrano in base a queste proprietà. Puoi omettere la clausola STORING (properties) se le tue query non ne traggono vantaggio.

CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;

L'esempio seguente utilizza un indice di etichette per velocizzare la corrispondenza dei nodi in base alle etichette.

CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);

L'esempio seguente aggiunge vincoli che applicano etichette e proprietà in minuscolo. Gli ultimi due esempi utilizzano la funzione JSON_KEYS. (Facoltativo) Puoi applicare il controllo delle lettere minuscole nella logica dell'applicazione.

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)));

Ottimizza gli aggiornamenti batch delle proprietà dinamiche con DML

La modifica delle proprietà dinamiche utilizzando funzioni come JSON_SET e JSON_REMOVE comporta operazioni di lettura-modifica-scrittura. Ciò può comportare costi più elevati rispetto all'aggiornamento delle proprietà di tipo STRING o INT64.

Se i workload prevedono aggiornamenti batch delle proprietà dinamiche utilizzando DML, utilizza i seguenti suggerimenti per ottenere prestazioni migliori:

  • Aggiorna più righe in una singola istruzione DML anziché elaborare le righe singolarmente.

  • Quando aggiorni un intervallo di chiavi ampio, raggruppa e ordina le righe interessate in base alle chiavi primarie. L'aggiornamento di intervalli non sovrapposti con ogni DML riduce la contesa dei blocchi.

  • Utilizza i parametri di query nelle istruzioni DML anziché codificarli in modo permanente per migliorare il rendimento.

In base a questi suggerimenti, l'esempio seguente mostra come aggiornare la proprietà is_blocked per 100 nodi in una singola istruzione DML. I parametri di query includono:

  1. @node_ids: chiavi di GraphNode righe, memorizzate in un parametro ARRAY. Se applicabile, il raggruppamento e l'ordinamento tra i DML consentono di ottenere prestazioni migliori.

  2. @is_blocked_values: i valori corrispondenti da aggiornare, memorizzati in un parametro ARRAY.

UPDATE GraphNode
SET properties = JSON_SET(
  properties,
  '$.is_blocked',
  CASE id
    WHEN @node_ids[OFFSET(0)] THEN @is_blocked_values[OFFSET(0)]
    WHEN @node_ids[OFFSET(1)] THEN @is_blocked_values[OFFSET(1)]
    ...
    WHEN @node_ids[OFFSET(99)] THEN @is_blocked_values[OFFSET(99)]
  END,
  create_if_missing => TRUE)
WHERE id IN UNNEST(@node_ids)

Risoluzione dei problemi

Questa sezione descrive come risolvere i problemi relativi ai dati senza schema.

La proprietà compare più volte nel risultato TO_JSON

Problema

Il nodo seguente modella le proprietà birthday e name come proprietà dinamiche nella colonna JSON. Nel risultato JSON dell'elemento del grafo vengono visualizzate proprietà birthday e name duplicate.

GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;

Vengono restituiti risultati simili ai seguenti:

{
  …,
  "properties": {
    "birthday": "1991-12-21 00:00:00",
    "name": "Alex",
    "id": 14,
    "label": "person",
    "properties": {
      "birthday": "1991-12-21 00:00:00",
      "name": "Alex"
    }
  }
  …
}

Possibile causa

Per impostazione predefinita, tutte le colonne della tabella di base sono definite come proprietà. L'utilizzo di TO_JSON o SAFE_TO_JSON per restituire elementi del grafico genera proprietà duplicate. Questo accade perché la colonna JSON (properties) è una proprietà definita dallo schema, mentre le chiavi di primo livello di JSON sono modellate come proprietà dinamiche.

Soluzione consigliata

Per evitare questo comportamento, utilizza la clausola PROPERTIES ALL COLUMNS EXCEPT per escludere la colonna properties quando definisci le proprietà nello schema, come mostrato nell'esempio seguente:

CREATE OR REPLACE PROPERTY GRAPH FinGraph
  NODE TABLES (
    GraphNode
      PROPERTIES ALL COLUMNS EXCEPT (properties)
      DYNAMIC LABEL (label)
      DYNAMIC PROPERTIES (properties)
  );

Dopo la modifica dello schema, gli elementi del grafico restituiti del tipo di dati JSON non presentano duplicati.

GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;

Questa query restituisce quanto segue:

{
  …
  "properties": {
    "birthday": "1991-12-21 00:00:00",
    "name": "Alex",
    "id": 1,
    "label": "person",
  }
}

Passaggi successivi