Questa pagina descrive come gestire i dati senza schema in Spanner Graph. Fornisce inoltre dettagli sulle best practice e sui suggerimenti per la risoluzione dei problemi. È consigliabile avere familiarità con lo schema e le query di Spanner Graph.
La gestione dei dati senza schema consente di creare una definizione flessibile di un grafico, in cui le definizioni dei tipi di nodi e archi possono essere aggiunte, aggiornate o eliminate senza modifiche allo schema. L'approccio supporta lo sviluppo iterativo e un overhead di gestione dello schema inferiore, preservando al contempo l'esperienza di query del grafico familiare.
La gestione dei dati senza schema è particolarmente utile per i seguenti scenari:
- Gestisci grafici con modifiche frequenti, ad esempio aggiornamenti e aggiunte di etichette e proprietà degli elementi.
- Il grafico ha molti tipi di nodi e archi, il che rende difficile la creazione e la gestione delle tabelle di input.
Modellare dati senza schema
Spanner Graph ti consente di creare un grafico da tabelle in cui le righe sono
mappate
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à.
Creare 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 e 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 operazioni:
Memorizza tutti i nodi in una singola tabella
GraphNode
, identificata in modo univoco daid
.Archivia tutti gli archi in una singola tabella
GraphEdge
, identificati in modo univoco dalla combinazione di origine (id
), destinazione (dest_id
) e dal proprio identificatore (edge_id
). Unedge_id
è incluso nella chiave primaria per consentire più di un arco da una coppiaid
adest_id
.
Le tabelle dei nodi e degli archi hanno le proprie colonne label
e properties
di tipo
STRING
e JSON
rispettivamente.
Creare un grafico delle proprietà
Con l'istruzione CREATE PROPERTY GRAPH, le tabelle di input nella sezione precedente vengono mappate come nodi e archi. Devi utilizzare 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 colonnaSTRING
della tabella di input.DYNAMIC PROPERTIES
: crea le proprietà di un nodo o di un arco da una colonnaJSON
della tabella di input.
Il seguente esempio 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)
);
Etichetta dinamica
La clausola DYNAMIC LABEL
designa una colonna di tipo di dati STRING
per memorizzare 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. Allo stesso modo, in una riga GraphEdge
, se
la colonna dell'etichetta ha un valore owns
, viene mappata a un arco Owns
all'interno del
grafico.
Proprietà dinamiche
La clausola DYNAMIC PROPERTIES
designa una colonna di tipo di dati JSON
per archiviare
le proprietà. Le chiavi JSON sono i nomi delle proprietà e i valori JSON sono i valori delle proprietà.
Ad esempio, quando la colonna properties
di una riga GraphNode
ha il valore JSON
'{"name": "David", "age": 43}'
, viene mappata a un nodo con le proprietà age
e name
, con 43
e "David"
come valori delle proprietà.
Quando non utilizzare la 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 dalle tabelle esistenti anziché introdurre nuove tabelle di nodi e archi dedicate.
- I limiti dei dati senza schema impediscono la tua 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
rispetto all'utilizzo di proprietà dinamiche con tipo JSON
.
Per ulteriori informazioni su come definire lo schema del grafico senza utilizzare etichette e proprietà dei dati dinamici, consulta la Panoramica dello schema del grafico Spanner.
Eseguire query sui dati del grafico senza schema
Puoi eseguire query sui dati del grafico 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.
Corrispondenza di nodi e archi utilizzando le etichette
Puoi trovare corrispondenze tra nodi e archi utilizzando l'espressione dell'etichetta in GQL.
La seguente query corrisponde a nodi e archi connessi che hanno i valori
account
e transfers
nella colonna delle etichette.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Proprietà di accesso
Le chiavi e i valori di primo livello del tipo di dati JSON
sono modellati come proprietà,
come age
e name
nell'esempio seguente.
JSON document |
Properties |
|
|
|
L'esempio seguente mostra come accedere alla proprietà name
dal nodo Person
.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
Vengono restituiti risultati simili ai seguenti:
JSON"Tom"
Convertire i tipi di dati delle proprietà
Le proprietà vengono trattate come valori del tipo di dati JSON. In alcuni casi, ad esempio per i confronti con i tipi SQL, devono prima essere convertiti in un tipo SQL.
Nell'esempio riportato di seguito, la query esegue le seguenti conversioni dei tipi di dati:
- Converte la proprietà
is_blocked
in un tipo booleano per valutare l'espressione. - Converte la proprietà
order_number_str
in un tipo di stringa e la confronta con il valore letterale"302290001255747"
. - Utilizza la funzione LAX_INT64
per convertire in modo sicuro
order_number_str
in un numero intero come tipo restituito.
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;
Vengono restituiti risultati simili ai seguenti:
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
In clausole come GROUP BY
e ORDER BY
, devi anche convertire il tipo di dati JSON. L'esempio seguente converte la proprietà city
in un tipo stringa,
consentendone l'utilizzo per il raggruppamento.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
Suggerimenti per la conversione dei tipi di dati JSON in tipi di dati SQL:
- I convertitori rigorosi, come
INT64
, eseguono controlli rigorosi su tipi e valori. Questa operazione è consigliata quando il tipo di dati JSON è noto e applicato, ad esempio utilizzando i vincoli dello schema per applicare il tipo di dati della proprietà. - I convertitori flessibili, ad esempio
LAX_INT64
, converte il valore in modo sicuro, quando possibile, e restituisceNULL
quando la conversione non è fattibile. Questa opzione è consigliata quando non è necessario un controllo rigoroso o i tipi sono difficili da applicare.
Scopri di più sulla conversione dei dati nei suggerimenti per la risoluzione dei problemi.
Filtrare per valori delle proprietà
Nei filtri delle proprietà,
i parametri di filtro vengono trattati come valori del tipo di dati JSON
. Ad esempio, nella
seguente query, is_blocked
viene trattato come un boolean
JSON e
order_number_str
come un string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
Vengono restituiti risultati simili ai seguenti:
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
Il parametro del filtro deve corrispondere al tipo e al valore della proprietà. Ad esempio, quando
il parametro di filtro order_number_str
è un numero intero, non viene trovata alcuna corrispondenza perché
la proprietà è un string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Accedere alle proprietà JSON nidificate
Le chiavi e i valori JSON nidificati non vengono modellati come proprietà. Nel seguente
esempio, le chiavi JSON city
, state
e country
non sono modellate 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 |
|
|
|
|
|
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 STRING(person.location.city);
Vengono restituiti risultati simili ai seguenti:
"New York"
Modificare i dati senza schema
Spanner Graph mappa i dati delle tabelle in nodi e archi del grafico. Quando modifichi i dati della tabella di input, vengono apportate modifiche dirette ai dati del grafico corrispondenti. Per ulteriori informazioni sulla mutazione dei dati del grafico, vedi Inserire, aggiornare o eliminare i dati di Spanner Graph.
Esempi
Questa sezione fornisce esempi su come creare, aggiornare ed eliminare i dati del grafico.
Inserire i dati del grafico
L'esempio seguente inserisce un nodo person
. I nomi delle etichette e delle proprietà devono
essere in minuscolo.
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 il bordo Transfers
sui nodi Account
che sono stati trasferiti agli 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
Questa sezione elenca le limitazioni dell'utilizzo della gestione dei dati senza schema.
Requisito di una singola tabella per l'etichetta dinamica
Puoi avere una sola tabella dei nodi se nella sua definizione viene utilizzata un'etichetta dinamica. Questa limitazione si applica anche alla tabella degli archi. Non è consentito 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 dei nodi o più tabelle degli archi che utilizzano ciascuna un'etichetta dinamica.
Ad esempio, il seguente codice non funziona 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 minuscoli
Per essere abbinati, i valori delle stringhe delle etichette devono essere memorizzati in minuscolo. Ti consigliamo di applicare questa regola nel codice dell'applicazione o utilizzando i vincoli dello schema.
Sebbene i valori delle stringhe delle etichette debbano essere memorizzati in minuscolo, non sono sensibili alle maiuscole quando vengono a cui si fa riferimento in una query.
Il seguente esempio mostra come inserire etichette in 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
I nomi delle proprietà devono essere memorizzati 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 sono sensibili alle maiuscole quando li fai riferimento nella query.
L'esempio seguente inserisce le proprietà name
e age
utilizzando le 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;
Altre limitazioni
- Solo le chiavi di primo livello del tipo di dati
JSON
vengono modellate come proprietà. - I tipi di dati delle proprietà devono essere conformi alle specifiche del tipo JSON di Spanner.
Best practice
Questa sezione descrive le best practice per modellare i dati senza schema.
Definizioni delle chiavi primarie per nodi e archi
La chiave di un nodo deve essere univoca in tutti i nodi del grafico. Ad esempio, come colonna INT64
o stringa
UUID
.
Se hai più archi tra due nodi, devi introdurre 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, puoi includere facoltativamente
la colonna label
come colonna di 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
il rendimento delle query filtrate solo per etichetta.
Per saperne di più sulla scelta della chiave primaria, vedi Scegliere una chiave primaria.
Indice secondario per una proprietà a cui si accede di frequente
Per migliorare le prestazioni delle query per una proprietà utilizzata di frequente nei filtri, puoi creare un indice secondario in base a una colonna della proprietà generata e poi utilizzarlo nello schema del grafico e nelle query.
L'esempio seguente aggiunge 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));
Quindi, crea un NULL FILTERED INDEX
per person_age
e lo intercala nella
tabella GraphNode
per l'accesso locale.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
La tabella GraphNode
ora include nuove colonne disponibili come proprietà dei nodi del grafico. Per riflettere questa situazione 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à.
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;
(Facoltativo) Puoi eseguire il comando
ANALYZE
dopo la creazione dell'indice in modo che l'ottimizzatore delle query venga aggiornato con le statistiche
del database più recenti.
Controllare i vincoli 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 suggerimenti per i vincoli di controllo che puoi utilizzare con i dati senza schema.
Applica valori etichetta validi
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 i valori delle etichette e i nomi delle proprietà in minuscolo
Poiché i nomi delle etichette e delle proprietà devono essere memorizzati come valori in minuscolo, ti consigliamo di procedere in uno dei seguenti modi:
- Applica il controllo nella logica dell'applicazione.
- Crea vincoli check nello schema.
Al momento della query, il nome dell'etichetta e della proprietà non fa distinzione tra maiuscole e minuscole.
L'esempio seguente aggiunge un vincolo di etichetta del nodo alla tabella GraphNode
per garantire che l'etichetta sia in minuscolo.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
L'esempio seguente aggiunge 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)));
Imporre l'esistenza della proprietà
Puoi creare un vincolo che controlla 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));
Forzare l'univocità della proprietà
Puoi creare vincoli basati sulle proprietà che verificano se la proprietà di un nodo o di un arco è univoca tra nodi o archi con la stessa etichetta. Per farlo, utilizza un UNIQUE INDEX rispetto alle colonne generate delle proprietà.
Nell'esempio seguente, l'indice univoco verifica che le proprietà name
e country
combinate siano univoche per qualsiasi nodo person
.
Aggiungi una colonna generata per
PersonName
.ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
Aggiungi una colonna generata per
PersonCountry
.ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
Crea un indice univoco
NULL_FILTERED
in base alle proprietàPersonName
ePersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Forzare i tipi di dati delle proprietà
Puoi applicare un tipo di dati di 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));
Combinare etichette definite e dinamiche
Spanner consente ai nodi del grafico delle proprietà di avere etichette definite (nello schema) ed etichette dinamiche (derivate dai dati). Puoi personalizzare 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)
);
Qui, ogni nodo creato da GraphNode
ha l'etichetta defined Entity
. Inoltre, ogni nodo ha un'etichetta dinamica determinata dal valore nella colonna
dell'etichetta.
Puoi quindi scrivere query che corrispondono 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 in base ai suoi dati.
Esempi di schema
Puoi utilizzare gli esempi di schema in questa sezione come modelli per creare i tuoi schemi. I componenti chiave dello schema includono:
- Creazione di tabelle di input del grafico
- Creazione del grafico delle proprietà
- (Facoltativo) Indice di attraversamento degli archi inverso per migliorare il rendimento dell'attraversamento inverso
- (Facoltativo) Etichetta l'indice per migliorare il rendimento delle query per etichette
- (Facoltativo) Vincoli dello schema da applicare a etichette in minuscolo e nomi delle proprietà
Il seguente esempio 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;
Il seguente esempio 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 impongono etichette e proprietà in minuscolo. Gli ultimi due esempi utilizzano la funzione
JSON_KEYS
. Se vuoi, 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)));
Ottimizzazione degli 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 e scrittura. Possono comportare costi più elevati rispetto
all'aggiornamento delle proprietà di tipo STRING
o INT64
.
Se i tuoi workload prevedono aggiornamenti batch delle proprietà dinamiche utilizzando DML, prendi in considerazione i seguenti consigli per ottenere un rendimento migliore:
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 aggiorna la proprietà is_blocked
per 100 nodi in un'unica istruzione DML. I parametri
della query includono:
@node_ids
: le chiavi delle righeGraphNode
, memorizzate in un parametroARRAY
. Se applicabile, il raggruppamento e l'ordinamento tra i DML consentono di ottenere un rendimento migliore.@is_blocked_values
: i valori corrispondenti da aggiornare, memorizzati in un parametroARRAY
.
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ù di una volta nel risultato TO_JSON
Problema
Il seguente nodo modella le proprietà birthday
e name
come proprietà dinamiche nella colonna JSON
. Le proprietà duplicate di
birthday
e name
vengono visualizzate nel risultato JSON dell'elemento del grafico.
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. Ciò è dovuto al fatto che la
colonna JSON
(ovvero 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 hanno 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",
}
}
Problemi comuni quando i valori delle proprietà non vengono convertiti correttamente
Una soluzione comune ai seguenti problemi è utilizzare sempre le conversioni dei valori delle proprietà quando si utilizza una proprietà all'interno di un'espressione di query.
Confronto dei valori delle proprietà senza conversione
Problema
No matching signature for operator = for argument types: JSON, STRING
Possibile causa
La query non converte correttamente i valori delle proprietà. Ad esempio, la proprietà name
non viene convertita nel tipo STRING
nel confronto:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Soluzione consigliata
Per risolvere il problema, utilizza una conversione di valori prima del confronto.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
Vengono restituiti risultati simili ai seguenti:
+------+
| id |
+------+
| 1 |
+------+
In alternativa, utilizza un filtro delle proprietà per semplificare
i confronti di uguaglianza in cui la conversione del valore viene eseguita automaticamente. Tieni presente
che il tipo di valore ("Alex") deve corrispondere esattamente al tipo STRING
della proprietà in
JSON
.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
Vengono restituiti risultati simili ai seguenti:
+------+
| id |
+------+
| 1 |
+------+
Utilizzo del valore della proprietà RETURN DISTINCT
senza conversione
Problema
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
Possibile causa
Nell'esempio seguente, order_number_str
non è stato convertito prima di essere
utilizzato nell'istruzione RETURN DISTINCT
:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
Soluzione consigliata
Per risolvere il problema, utilizza una conversione di valore prima del giorno RETURN DISTINCT
.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
Vengono restituiti risultati simili ai seguenti:
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
Proprietà utilizzata come chiave di raggruppamento senza conversione
Problema
Grouping by expressions of type JSON is not allowed.
Possibile causa
Nell'esempio seguente, t.order_number_str
non viene convertito prima di essere utilizzato
per raggruppare gli oggetti JSON:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
Soluzione consigliata
Per risolvere il problema, utilizza una conversione del valore prima di utilizzare la proprietà come chiave di raggruppamento.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
Vengono restituiti risultati simili ai seguenti:
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
Proprietà utilizzata come chiave di ordinamento senza conversione
Problema
ORDER BY does not support expressions of type JSON
Possibile causa
Nell'esempio seguente, t.amount
non viene convertito prima di essere utilizzato per
ordinare i risultati:
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;
Soluzione consigliata
Per risolvere il problema, esegui una conversione su t.amount
nella clausola 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 DOUBLE(t.amount) DESC
LIMIT 1;
Vengono restituiti risultati simili ai seguenti:
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
Mancata corrispondenza del tipo durante la conversione
Problema
The provided JSON input is not an integer
Possibile causa
Nell'esempio seguente, la proprietà order_number_str
viene memorizzata come tipo di dati JSON
STRING
. Se provi a eseguire una conversione in INT64
, viene restituito un
errore.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Soluzione consigliata
Per risolvere il problema, utilizza il convertitore di valori esatti che corrisponde al tipo di valore.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
Vengono restituiti risultati simili ai seguenti:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
In alternativa, utilizza un convertitore flessibile quando il valore è convertibile nel tipo di destinazione, come mostrato nell'esempio seguente:
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Vengono restituiti risultati simili ai seguenti:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Passaggi successivi
- Per saperne di più su JSON, consulta Modificare i dati JSON e Elenco delle funzioni JSON.
- Confronta Spanner Graph e openCypher.
- Esegui la migrazione a Spanner Graph.