Esta página explica como gerir dados sem esquema no Spanner Graph. Também oferece práticas recomendadas e sugestões de resolução de problemas. Recomendamos que se familiarize com o esquema do Spanner Graph e as consultas.
A gestão de dados sem esquema permite-lhe criar uma definição de gráfico flexível. Pode adicionar, atualizar ou eliminar definições de tipos de nós e arestas sem alterar o esquema. Esta abordagem suporta o desenvolvimento iterativo e reduz a sobrecarga de gestão de esquemas, ao mesmo tempo que preserva a experiência de consulta de gráficos familiar.
A gestão de dados sem esquema é útil para os seguintes cenários:
Gerir gráficos com alterações frequentes, como atualizações e adições de etiquetas e propriedades de elementos.
Gráficos com muitos tipos de nós e arestas, o que torna a criação e a gestão de tabelas de entrada difíceis.
Para mais informações sobre quando usar a gestão de dados sem esquema, consulte as Considerações para a gestão de dados sem esquema.
Modele dados sem esquema
O Spanner Graph permite-lhe criar um gráfico a partir de tabelas que mapeiam linhas para nós e arestas.
Em vez de usar tabelas separadas para cada tipo de elemento, a modelagem de dados sem esquema usa normalmente uma única tabela de nós e uma única tabela de arestas com uma coluna STRING
para a etiqueta e uma coluna JSON
para as propriedades.
Crie tabelas de entrada
Pode criar uma única tabela GraphNode
e uma única tabela GraphEdge
para armazenar dados sem esquema, conforme mostrado no exemplo seguinte. Os nomes das tabelas são para fins ilustrativos. Pode escolher os seus próprios nomes.
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;
Este exemplo realiza as seguintes ações:
Armazena todos os nós numa única tabela,
GraphNode
, identificada por umid
único.Armazena todas as arestas numa única tabela,
GraphEdge
, identificada por uma combinação única de origem (id
), destino (dest_id
) e o seu próprio identificador (edge_id
). É incluído umedge_id
como parte da chave primária para permitir mais do que uma aresta de um parid
para um pardest_id
.
As tabelas de nós e arestas têm as suas próprias colunas label
e properties
.
Estas colunas são do tipo STRING
e JSON
, respetivamente.
Para mais informações sobre as principais opções de gestão de dados sem esquema, consulte os artigos Definições de chave primária para nós e arestas.
Crie um gráfico de propriedades
A declaração CREATE PROPERTY GRAPH mapeia as tabelas de entrada na secção anterior como nós e arestas. Use as seguintes cláusulas para definir etiquetas e propriedades para dados sem esquema:
DYNAMIC LABEL
: cria a etiqueta de um nó ou de uma aresta a partir de umaSTRING
coluna da tabela de entrada.DYNAMIC PROPERTIES
: cria propriedades de um nó ou de uma aresta a partir de umaJSON
coluna da tabela de entrada.
O exemplo seguinte mostra como criar um gráfico com estas cláusulas:
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)
);
Defina etiquetas dinâmicas
A cláusula DYNAMIC LABEL
designa uma coluna do tipo de dados STRING
para armazenar os valores das etiquetas.
Por exemplo, numa linha GraphNode
, se a coluna label
tiver um valor person
, é mapeada para um nó Person
no gráfico. Da mesma forma, numa linha GraphEdge
, se a coluna de etiqueta tiver um valor de owns
, é mapeada para uma aresta Owns
no gráfico.
Para mais informações sobre as limitações ao usar etiquetas dinâmicas, consulte a secção Limitações.
Defina propriedades dinâmicas
A cláusula DYNAMIC PROPERTIES
designa uma coluna do tipo de dados JSON
para armazenar propriedades. As chaves JSON representam nomes de propriedades e os valores JSON representam valores de propriedades.
Por exemplo, quando a coluna properties
de uma linha GraphNode
tem o valor JSON '{"name": "David", "age": 43}'
, o Spanner mapeia-o para um nó que tem propriedades age
e name
com 43
e "David"
como os respetivos valores.
Considerações para a gestão de dados sem esquema
Pode ser preferível não usar a gestão de dados sem esquema nos seguintes cenários:
- Os tipos de nós e arestas dos seus dados de grafos estão bem definidos ou as respetivas etiquetas e propriedades não requerem atualizações frequentes.
- Os seus dados já estão armazenados no Spanner e prefere criar gráficos a partir de tabelas existentes em vez de introduzir novas tabelas de nós e arestas dedicadas.
- As limitações dos dados sem esquema impedem a adoção.
Além disso, se a sua carga de trabalho for altamente sensível ao desempenho de escrita, especialmente quando as propriedades são atualizadas com frequência, a utilização de propriedades definidas pelo esquema com tipos de dados primitivos, como STRING
ou INT64
, é mais eficaz do que a utilização de propriedades dinâmicas com o tipo JSON
.
Para mais informações sobre como definir o esquema do gráfico sem usar etiquetas e propriedades de dados dinâmicos, consulte a vista geral do esquema do gráfico do Spanner.
Consultar dados de gráficos sem esquema
Pode consultar dados de grafos sem esquemas através da linguagem de consulta de grafos (GQL). Pode usar as consultas de exemplo na vista geral da consulta de gráficos do Spanner e na referência do GQL com modificações limitadas.
Faça a correspondência de nós e arestas através de etiquetas
Pode fazer corresponder nós e arestas usando a expressão de etiqueta no GQL.
A seguinte consulta corresponde a nós e arestas ligados que têm os valores
account
e transfers
na respetiva coluna de etiqueta.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Aceda às propriedades
O Spanner modela as chaves e os valores de nível superior do tipo de dados JSON
como propriedades, como age
e name
no exemplo seguinte.
JSON document |
Properties |
|
|
|
O exemplo seguinte mostra como aceder à propriedade name
a partir do nó Person
.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
A consulta devolve resultados semelhantes aos seguintes:
JSON"Tom"
Converta tipos de dados de propriedades
O Spanner trata as propriedades como valores do tipo de dados JSON. Em alguns casos, como para comparações com tipos SQL, tem primeiro de converter propriedades num tipo SQL.
No exemplo seguinte, a consulta faz as seguintes conversões de tipo de dados:
- Converte a propriedade
is_blocked
num tipo booleano para avaliar a expressão. - Converte a propriedade
order_number_str
num tipo de string e compara-a com o valor literal"302290001255747"
. - Usa a função LAX_INT64 para converter em segurança
order_number_str
num número inteiro como o tipo de retorno.
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;
Isto devolve resultados semelhantes aos seguintes:
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
Em cláusulas como GROUP BY
e ORDER BY
, também tem de converter o tipo de dados JSON. O exemplo seguinte converte a propriedade city
num tipo de string,
o que lhe permite usá-la para o agrupamento.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
Sugestões para converter tipos de dados JSON em tipos de dados SQL:
- Os conversores rigorosos, como
INT64
, fazem verificações rigorosas de tipo e valor. Use conversores rigorosos quando o tipo de dados JSON for conhecido e aplicado, por exemplo, usando restrições de esquema para aplicar o tipo de dados da propriedade. - Os conversores flexíveis, como
LAX_INT64
, convertem o valor em segurança sempre que possível e devolvemNULL
quando a conversão não é viável. Use conversores flexíveis quando não for necessária uma verificação rigorosa ou quando os tipos forem difíceis de aplicar.
Para mais informações sobre a conversão de dados, consulte as sugestões de resolução de problemas.
Filtre por valores de propriedades
Nos filtros de propriedades, o Spanner trata os parâmetros de filtro como valores do tipo de dados JSON
. Por exemplo, na seguinte consulta, o Spanner
tratais_blocked
como um JSON boolean
e order_number_str
como um JSON
string
.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
Isto devolve resultados semelhantes aos seguintes:
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
O parâmetro de filtro tem de corresponder ao tipo e ao valor da propriedade. Por exemplo, quando o parâmetro de filtro order_number_str
é um número inteiro, o Spanner não encontra nenhuma correspondência porque a propriedade é um JSON string
.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Aceda a propriedades JSON aninhadas
O Spanner não modela chaves e valores JSON aninhados como propriedades.
No exemplo seguinte, o Spanner não modela as chaves JSON city
, state
e country
como propriedades porque estão aninhadas em location
. No entanto, pode aceder a eles com um operador de acesso a campos JSON ou um operador de subscrição JSON.
JSON document |
Properties |
|
|
|
|
|
O exemplo seguinte mostra como aceder a propriedades aninhadas com o operador de acesso do campo JSON.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
Isto devolve resultados semelhantes aos seguintes:
"New York"
Modifique dados sem esquema
O gráfico do Spanner mapeia dados de tabelas para nós e arestas do gráfico. Quando altera os dados da tabela de entrada, esta alteração provoca diretamente mutações nos dados do gráfico correspondentes. Para mais informações sobre a mutação de dados de grafos, consulte o artigo Inserir, atualizar ou eliminar dados de grafos do Spanner.
Consultas de exemplo
Esta secção fornece exemplos que mostram como criar, atualizar e eliminar dados de gráficos.
Inserir dados do gráfico
O exemplo seguinte insere um nó person
. Os nomes de etiquetas e propriedades têm de usar letras minúsculas.
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
Atualize os dados do gráfico
O exemplo seguinte atualiza um nó Account
e usa a função
JSON_SET
para definir a respetiva propriedade is_blocked
.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
O exemplo seguinte atualiza um nó person
com um novo conjunto de propriedades.
UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;
O exemplo seguinte usa a função
JSON_REMOVE
para remover a propriedade is_blocked
de um nó Account
. Após a execução, todas as outras propriedades existentes permanecem inalteradas.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
Elimine os dados do gráfico
O exemplo seguinte elimina a aresta Transfers
em nós Account
que foram transferidos para contas bloqueadas.
DELETE FROM GraphEdge
WHERE label = "transfers" AND id IN {
GRAPH FinGraph
MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
RETURN a.id
}
Limitações conhecidas
Esta secção apresenta as limitações da utilização da gestão de dados sem esquema.
Requisito de tabela única para etiquetas dinâmicas
Só pode ter uma tabela de nós se for usada uma etiqueta dinâmica na respetiva definição. Esta restrição também se aplica à tabela de arestas. O Spanner não permite o seguinte:
- Definir uma tabela de nós com uma etiqueta dinâmica juntamente com quaisquer outras tabelas de nós.
- Definir uma tabela de limites com uma etiqueta dinâmica juntamente com quaisquer outras tabelas de limites.
- Definir várias tabelas de nós ou várias tabelas de arestas que usam cada uma uma etiqueta dinâmica.
Por exemplo, o seguinte código falha quando tenta criar vários nós do gráfico com etiquetas dinâmicas.
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 (
...
);
Os nomes das etiquetas têm de estar em minúsculas
Tem de armazenar os valores de strings de etiquetas em letras minúsculas para correspondência. Recomendamos que aplique esta regra no código da aplicação ou através de restrições do esquema.
Embora os valores de string das etiquetas tenham de ser armazenados em letras minúsculas, não são sensíveis a maiúsculas e minúsculas quando lhes faz referência numa consulta.
O exemplo seguinte mostra como inserir etiquetas em valores com letras minúsculas:
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
Pode usar etiquetas não sensíveis a maiúsculas e minúsculas para corresponder ao GraphNode
ou ao GraphEdge
.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
Os nomes das propriedades têm de estar em minúsculas
Tem de armazenar os nomes das propriedades em letras minúsculas. Recomendamos que aplique esta regra no código da aplicação ou através de restrições de esquema.
Embora os nomes das propriedades tenham de ser armazenados em letras minúsculas, não são sensíveis a maiúsculas e minúsculas quando lhes faz referência na sua consulta.
O exemplo seguinte insere as propriedades name
e age
com letras minúsculas.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
No texto da consulta, os nomes das propriedades não são sensíveis a maiúsculas e minúsculas. Por exemplo, pode usar Age
ou age
para aceder à propriedade.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
Limitações adicionais
- Os modelos do Spanner só usam chaves de nível superior do tipo de dados
JSON
como propriedades. - Os tipos de dados de propriedades têm de estar em conformidade com as especificações do tipo JSON do Spanner.
Práticas recomendadas para dados sem esquema
Esta secção descreve as práticas recomendadas que ajudam a modelar dados sem esquema.
Defina chaves principais para nós e arestas
A chave de um nó deve ser exclusiva em todos os nós do gráfico. Por exemplo, como uma coluna INT64
ou string
UUID
.
Se existirem várias arestas entre dois nós, introduza um identificador único para a aresta. O exemplo de esquema usa uma coluna INT64
edge_id
de lógica de aplicação.
Quando cria o esquema para tabelas de nós e arestas, inclua opcionalmente a coluna label
como uma coluna de chave principal se o valor for imutável. Se o fizer, a chave composta formada por todas as colunas principais deve ser única em todos os nós ou arestas. Esta técnica melhora o desempenho das consultas que são filtradas apenas por etiqueta.
Para mais informações sobre a escolha da chave principal, consulte o artigo Escolha uma chave principal.
Crie um índice secundário para uma propriedade acedida com frequência
Para aumentar o desempenho das consultas de uma propriedade usada frequentemente em filtros, crie um índice secundário em relação a uma coluna de propriedade gerada. Em seguida, use-o num esquema de gráfico e em consultas.
O exemplo seguinte mostra como adicionar uma coluna age
gerada à tabela GraphNode
para um nó person
. O valor é NULL
para nós sem a etiqueta person
.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
A declaração DDL seguinte cria um NULL FILTERED INDEX
para
person_age
e intercala-o na tabela GraphNode
para acesso local.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
A tabela GraphNode
inclui novas colunas que estão disponíveis como propriedades do nó do gráfico. Para refletir isto na definição do gráfico de propriedades, use a declaração CREATE OR
REPLACE PROPERTY GRAPH
. Isto recompila a definição e inclui a coluna person_age
como uma propriedade.
Para mais informações, consulte o artigo sobre como atualizar as definições de nós ou arestas existentes.
A seguinte declaração recompila a definição e inclui a nova coluna person_age
como uma propriedade.
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)
);
O exemplo seguinte executa uma consulta com a propriedade indexada.
GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;
Opcionalmente, execute o comando ANALYZE
após a criação do índice para que o otimizador de consultas seja atualizado com as estatísticas da base de dados mais recentes.
Use restrições de verificação para a integridade dos dados
O Spanner suporta objetos de esquema, como restrições de verificação, para aplicar a integridade dos dados de etiquetas e propriedades. Esta secção apresenta recomendações para restrições de verificação que pode usar com dados sem esquema.
Aplique valores de etiquetas
Recomendamos que use NOT NULL
na definição da coluna de etiquetas para evitar
valores de etiquetas indefinidos.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
Aplique nomes de propriedades e valores de etiquetas em letras minúsculas
Uma vez que os nomes das etiquetas e das propriedades têm de ser armazenados como valores em minúsculas, faça uma das seguintes ações:
- Aplique a verificação na lógica da aplicação.
- Crie restrições de verificação no esquema.
No momento da consulta, o nome da propriedade e da etiqueta não é sensível a maiúsculas e minúsculas.
O exemplo seguinte mostra como adicionar uma restrição de etiqueta de nó à tabela GraphNode
para garantir que a etiqueta está em letras minúsculas.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
O exemplo seguinte mostra como adicionar uma restrição de verificação ao nome da propriedade de aresta. A verificação usa
JSON_KEYS
para
aceder às teclas de nível superior.
COALESCE
converte o resultado num conjunto vazio se JSON_KEYS
devolver NULL
e, em seguida,
verifica se cada chave está em minúsculas.
ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
Imponha a existência de propriedades
Crie uma restrição que verifique se existe uma propriedade para uma etiqueta.
No exemplo seguinte, a restrição verifica se um nó person
tem uma propriedade name
.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
Aplique propriedades únicas
Crie restrições baseadas em propriedades que verificam se a propriedade de um nó ou um limite é única em nós ou limites com a mesma etiqueta. Para o fazer, use um UNIQUE INDEX em relação às colunas geradas de propriedades.
No exemplo seguinte, o índice exclusivo verifica se as propriedades name
e country
combinadas são exclusivas para qualquer nó person
.
Adicione uma coluna gerada para
PersonName
.ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
Adicione uma coluna gerada para
PersonCountry
.ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
Crie um
NULL_FILTERED
índice exclusivo em relação às propriedadesPersonName
ePersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Aplique o tipo de dados da propriedade
Aplique um tipo de dados de propriedade usando uma restrição de tipo de dados num valor de propriedade
para uma etiqueta, conforme mostrado no exemplo seguinte. Este exemplo usa a função
JSON_TYPE
para verificar se a propriedade name
da etiqueta person
usa o tipo
STRING
.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
Combine etiquetas definidas e dinâmicas
O Spanner permite que os nós no gráfico de propriedades tenham etiquetas definidas (definidas no esquema) e etiquetas dinâmicas (derivadas de dados). Personalize as etiquetas para usar esta flexibilidade.
Considere o seguinte esquema que mostra a criação da tabela 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)
);
Aqui, todos os nós criados a partir de GraphNode
têm a etiqueta definida Entity
. Além disso, cada nó tem uma etiqueta dinâmica determinada pelo valor na respetiva coluna de etiqueta.
Em seguida, escreva consultas que correspondam a nós com base no tipo de etiqueta. Por exemplo, a seguinte consulta encontra nós que usam a etiqueta Entity
definida:
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
Embora esta consulta use a etiqueta definida Entity
, lembre-se de que o nó correspondente também tem uma etiqueta dinâmica com base nos respetivos dados.
Exemplos de esquemas
Use os exemplos de esquemas nesta secção como modelos para criar os seus próprios esquemas. Os principais componentes do esquema incluem o seguinte:
- Criação de tabelas de entrada de gráficos
- Criação de gráficos de propriedades
- Opcional: inverta o índice de deslocamento de arestas para aumentar o desempenho do deslocamento inverso
- Opcional: etiquete o índice para aumentar o desempenho das consultas por etiquetas
- Opcional: restrições de esquema para aplicar etiquetas em letras minúsculas e nomes de propriedades
O exemplo seguinte mostra como criar tabelas de entrada e um gráfico de propriedades:
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)
);
O exemplo seguinte usa um índice para melhorar a travessia de arestas invertidas. A cláusula
STORING (properties)
inclui uma cópia das propriedades de limite, o que acelera as consultas que filtram com base nestas propriedades. Pode omitir a cláusula STORING (properties)
se as suas consultas não beneficiarem da mesma.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
O exemplo seguinte usa um índice de etiquetas para acelerar a correspondência de nós por etiquetas.
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
O exemplo seguinte adiciona restrições que aplicam etiquetas e propriedades em letras minúsculas. Os dois últimos exemplos usam a função
JSON_KEYS
. Opcionalmente, pode aplicar a verificação de letras minúsculas na lógica da aplicação.
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)));
Otimize as atualizações em lote de propriedades dinâmicas com DML
A modificação de propriedades dinâmicas através de funções como
JSON_SET
e
JSON_REMOVE
envolve operações de leitura-modificação-gravação. Isto pode levar a um custo mais elevado em comparação com a atualização das propriedades do tipo STRING
ou INT64
.
Se as cargas de trabalho envolverem atualizações em lote de propriedades dinâmicas através de DML, use as seguintes recomendações para alcançar um melhor desempenho:
Atualize várias linhas numa única instrução DML, em vez de processar linhas individualmente.
Quando atualiza um intervalo de chaves amplo, agrupe e ordene as linhas afetadas por chaves principais. A atualização de intervalos não sobrepostos com cada DML reduz a contenção de bloqueios.
Use parâmetros de consulta em declarações DML em vez de os codificar de forma rígida para melhorar o desempenho.
Com base nestas sugestões, o exemplo seguinte mostra como atualizar a propriedade is_blocked
para 100 nós numa única declaração DML. Os parâmetros
de consulta incluem o seguinte:
@node_ids
: chaves deGraphNode
linhas, armazenadas num parâmetroARRAY
. Se aplicável, agrupá-los e ordená-los em vários DMLs alcança um melhor desempenho.@is_blocked_values
: os valores correspondentes a atualizar, armazenados num parâmetroARRAY
.
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)
Resolver problemas
Esta secção descreve como resolver problemas com dados sem esquema.
A propriedade aparece várias vezes no resultado TO_JSON
Problema
O nó seguinte modela as propriedades birthday
e name
como propriedades dinâmicas na respetiva coluna JSON
. As propriedades birthday
e name
duplicadas
aparecem no resultado JSON do elemento do gráfico.
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
Isto devolve resultados semelhantes aos seguintes:
{
…,
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 14,
"label": "person",
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex"
}
}
…
}
Causa possível
Por predefinição, todas as colunas da tabela de base são definidas como propriedades. A utilização de
TO_JSON
ou
SAFE_TO_JSON
para devolver elementos do gráfico resulta em propriedades duplicadas. Isto ocorre porque a coluna JSON
(properties
) é uma propriedade definida pelo esquema, enquanto as chaves de primeiro nível do JSON
são modeladas como propriedades dinâmicas.
Solução recomendada
Para evitar este comportamento, use a cláusula PROPERTIES ALL COLUMNS EXCEPT
para excluir a coluna properties
quando definir propriedades no esquema, conforme mostrado no exemplo seguinte:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Após a alteração do esquema, os elementos do gráfico devolvidos do tipo de dados JSON
não têm duplicados.
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
Esta consulta devolve o seguinte:
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
Problemas comuns quando os valores das propriedades não são convertidos corretamente
Para corrigir os seguintes problemas, use sempre conversões de valores de propriedades quando usar uma propriedade numa expressão de consulta.
Comparação de valores de propriedades sem conversão
Problema
No matching signature for operator = for argument types: JSON, STRING
Causa possível
A consulta não converte corretamente os valores das propriedades. Por exemplo, a propriedade name
não é convertida no tipo STRING
na comparação:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Solução recomendada
Para corrigir este problema, use uma conversão de valor antes da comparação.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
Isto devolve resultados semelhantes aos seguintes:
+------+
| id |
+------+
| 1 |
+------+
Em alternativa, use um filtro de propriedade para simplificar as comparações de igualdade em que a conversão de valores ocorre automaticamente. Tenha em atenção que o tipo do valor ("Alex") tem de corresponder exatamente ao tipo de STRING
da propriedade em JSON
.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
Isto devolve resultados semelhantes aos seguintes:
+------+
| id |
+------+
| 1 |
+------+
Uso do valor da propriedade RETURN DISTINCT
sem conversão
Problema
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
Causa possível
No exemplo seguinte, order_number_str
não foi convertido antes de ser usado na declaração RETURN DISTINCT
:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
Solução recomendada
Para corrigir este problema, use uma conversão de valor antes de RETURN DISTINCT
.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
Isto devolve resultados semelhantes aos seguintes:
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
Propriedade usada como chave de agrupamento sem conversão
Problema
Grouping by expressions of type JSON is not allowed.
Causa possível
No exemplo seguinte, t.order_number_str
não é convertido antes de ser usado para agrupar objetos JSON:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
Solução recomendada
Para corrigir este problema, use uma conversão de valor antes de usar a propriedade como uma chave de agrupamento.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
Isto devolve resultados semelhantes aos seguintes:
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
Propriedade usada como chave de ordenação sem conversão
Problema
ORDER BY does not support expressions of type JSON
Causa possível
No exemplo seguinte, t.amount
não é convertido antes de ser usado para
ordenar os resultados:
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;
Solução recomendada
Para corrigir este problema, faça uma conversão em t.amount
na cláusula 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;
Isto devolve resultados semelhantes aos seguintes:
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
Falta de correspondência de tipo durante a conversão
Problema
The provided JSON input is not an integer
Causa possível
No exemplo seguinte, a propriedade order_number_str
é armazenada como um tipo de dados JSON
STRING
. Se tentar fazer uma conversão para INT64
, é devolvido um erro.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Solução recomendada
Para corrigir este problema, use o conversor de valores exatos que corresponda ao tipo de valor.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
Isto devolve resultados semelhantes aos seguintes:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Em alternativa, use um conversor flexível quando o valor for convertível no tipo de destino, conforme mostrado no exemplo seguinte:
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Isto devolve resultados semelhantes aos seguintes:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
O que se segue?
- Para saber mais sobre o JSON, consulte os artigos Modificar dados JSON e Lista de funções JSON.
- Compare o Spanner Graph e o openCypher.
- Migre para o Spanner Graph.