Nesta página, explicamos como gerenciar dados sem esquema no Spanner Graph. Ele também oferece práticas recomendadas e dicas de solução de problemas. Recomendamos que você conheça o esquema e as consultas do Spanner Graph.
Com o gerenciamento de dados sem esquema, é possível criar uma definição de gráfico flexível. É possível adicionar, atualizar ou excluir definições de tipo de nó e aresta sem alterações de esquema. Essa abordagem oferece suporte ao desenvolvimento iterativo e reduz a sobrecarga de gerenciamento de esquema, preservando a experiência familiar de consulta de gráficos.
O gerenciamento de dados sem esquema é útil para os seguintes cenários:
Gerenciar gráficos com mudanças frequentes, como atualizações e adições de rótulos e propriedades de elementos.
Gráficos com muitos tipos de nós e arestas, o que dificulta a criação e o gerenciamento de tabelas de entrada.
Para mais informações sobre quando usar o gerenciamento de dados sem esquema, consulte Considerações sobre o gerenciamento de dados sem esquema.
Modelar dados sem esquema
Com o Spanner Graph, é possível criar um gráfico de tabelas que mapeia linhas para nós e arestas.
Em vez de usar tabelas separadas para cada tipo de elemento, a modelagem de dados sem esquema geralmente emprega uma única tabela de nós e uma única tabela de arestas com uma coluna STRING
para o rótulo e uma coluna JSON
para propriedades.
Criar tabelas de entrada
É possível criar uma única tabela GraphNode
e uma única tabela GraphEdge
para
armazenar dados sem esquema, como mostrado no exemplo a seguir. Os nomes das tabelas são apenas para fins ilustrativos. Você pode escolher os seus.
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 em uma única tabela,
GraphNode
, identificada por umid
exclusivo.Armazena todas as arestas em uma única tabela,
GraphEdge
, identificada por uma combinação exclusiva de origem (id
), destino (dest_id
) e identificador próprio (edge_id
). Umedge_id
é incluído como parte da chave primária para permitir mais de uma aresta de um parid
paradest_id
.
As tabelas de nós e arestas têm colunas label
e properties
próprias.
Essas colunas são do tipo STRING
e JSON
, respectivamente.
Para mais informações sobre as opções de chave para gerenciamento de dados sem esquema, consulte Definições de chave primária para nós e arestas.
Criar um gráfico de propriedades
A instrução CREATE PROPERTY GRAPH mapeia as tabelas de entrada na seção anterior como nós e arestas. Use as cláusulas a seguir para definir rótulos e propriedades para dados sem esquema:
DYNAMIC LABEL
: cria o rótulo de um nó ou uma aresta de uma colunaSTRING
da tabela de entrada.DYNAMIC PROPERTIES
: cria propriedades de um nó ou uma aresta de uma colunaJSON
da tabela de entrada.
O exemplo a seguir mostra como criar um gráfico usando essas 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)
);
Definir rótulos dinâmicos
A cláusula DYNAMIC LABEL
designa uma coluna do tipo de dados STRING
para armazenar os valores de rótulo.
Por exemplo, em uma linha GraphNode
, se a coluna label
tiver um valor person
,
ela será mapeada para um nó Person
no gráfico. Da mesma forma, em uma linha GraphEdge
, se a coluna de rótulo tiver um valor de owns
, ela será mapeada para uma aresta Owns
no gráfico.
Para mais informações sobre limitações ao usar rótulos dinâmicos, consulte Limitações.
Definir propriedades dinâmicas
A cláusula DYNAMIC PROPERTIES
designa uma coluna de 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 a mapeia para um nó com propriedades age
e name
com 43
e "David"
como valores respectivos.
Considerações sobre o gerenciamento de dados sem esquema
Talvez você não queira usar o gerenciamento de dados sem esquema nos seguintes cenários:
- Os tipos de nós e arestas dos dados de gráfico estão bem definidos, ou os rótulos e propriedades não exigem atualizações frequentes.
- Seus dados já estão armazenados no Spanner, e você prefere criar gráficos com tabelas atuais 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 sua carga de trabalho for muito sensível à performance de gravação, especialmente quando as propriedades são atualizadas com frequência, usar propriedades definidas pelo esquema com tipos de dados primitivos, como STRING
ou INT64
, é mais eficaz do que usar propriedades dinâmicas com o tipo JSON
.
Para mais informações sobre como definir o esquema de gráfico sem usar rótulos e propriedades de dados dinâmicos, consulte a visão geral do esquema do Spanner Graph.
Consultar dados de gráfico sem esquema
É possível consultar dados de gráficos sem esquema usando a Graph Query Language (GQL). É possível usar as consultas de exemplo na Visão geral da consulta do Spanner Graph e na Referência do GQL com modificações limitadas.
Fazer a correspondência de nós e arestas usando rótulos
É possível corresponder nós e arestas usando a expressão de rótulo em GQL.
A consulta a seguir corresponde a nós e arestas conectados que têm os valores
account
e transfers
na coluna de rótulo.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Acessar propriedades
O Spanner modela chaves e valores de nível superior do tipo de dados JSON
como propriedades, como age
e name
no exemplo a seguir.
JSON document |
Properties |
|
|
|
O exemplo a seguir mostra como acessar a propriedade name
do nó Person
.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
A consulta retorna resultados semelhantes aos seguintes:
JSON"Tom"
Converter tipos de dados de propriedades
O Spanner trata as propriedades como valores do tipo de dados JSON. Em alguns casos, como comparações com tipos SQL, primeiro é necessário converter propriedades em um tipo SQL.
No exemplo a seguir, a consulta realiza as seguintes conversões de tipo de dados:
- Converte a propriedade
is_blocked
em um tipo booleano para avaliar a expressão. - Converte a propriedade
order_number_str
em um tipo de string e a compara com o valor literal"302290001255747"
. - Usa a função LAX_INT64 para converter
order_number_str
com segurança em um 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;
Isso retorna resultados semelhantes a estes:
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
Em cláusulas como GROUP BY
e ORDER BY
, também é necessário converter o tipo de dados JSON. O exemplo a seguir converte a propriedade city
em um tipo de string,
que permite usá-la para agrupamento.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
Dicas para converter tipos de dados JSON em tipos de dados SQL:
- Conversores estritos, como
INT64
, realizam verificações rigorosas de tipo e valor. Use conversores estritos 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. - Conversores flexíveis, como
LAX_INT64
, convertem o valor com segurança quando possível e retornamNULL
quando a conversão não é viável. Use conversores flexíveis quando uma verificação rigorosa não for necessária ou quando os tipos forem difíceis de aplicar.
Para mais informações sobre a conversão de dados, consulte dicas de solução de problemas.
Filtrar por valores de propriedade
Nos filtros de propriedade, o Spanner trata os parâmetros de filtro como valores do tipo de dados JSON
. Por exemplo, na consulta a seguir, o Spanner trata is_blocked
como um boolean
JSON e order_number_str
como um string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
Isso retorna resultados semelhantes a estes:
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
O parâmetro de filtro precisa 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 correspondência porque a propriedade é um string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Acessar propriedades JSON aninhadas
O Spanner não modela chaves e valores JSON aninhados como propriedades.
No exemplo a seguir, o Spanner não modela as chaves JSON city
, state
e country
como propriedades porque elas estão aninhadas em location
. No entanto, é possível acessá-los com um operador de acesso a campo ou um operador de subscrito JSON.
JSON document |
Properties |
|
|
|
|
|
O exemplo a seguir mostra como acessar propriedades aninhadas com o operador de acesso do campo JSON.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
Isso retorna resultados semelhantes a estes:
"New York"
Modificar dados sem esquema
O Spanner Graph mapeia dados de tabelas para nós e arestas de gráficos. Quando você muda os dados da tabela de entrada, essa mudança causa mutações diretamente nos dados do gráfico correspondente. Para mais informações sobre mutação de dados de gráficos, consulte Inserir, atualizar ou excluir dados do Spanner Graph.
Exemplo de consultas
Nesta seção, fornecemos exemplos que mostram como criar, atualizar e excluir dados de gráfico.
Inserir dados do gráfico
O exemplo a seguir insere um nó person
. Os nomes de rótulos e propriedades precisam usar letras minúsculas.
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
Atualizar dados do gráfico
O exemplo a seguir atualiza um nó Account
e usa a função
JSON_SET
para definir a propriedade is_blocked
dele.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
O exemplo a seguir 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 a seguir 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 permanecem inalteradas.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
Excluir dados de gráficos
O exemplo a seguir exclui 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 seção lista as limitações do uso do gerenciamento de dados sem esquema.
Requisito de tabela única para rótulos dinâmicos
Só é possível ter uma tabela de nós se um rótulo dinâmico for usado na definição dela. Essa restrição também se aplica à tabela de arestas. O Spanner não permite o seguinte:
- Definir uma tabela de nós com um rótulo dinâmico ao lado de qualquer outra tabela de nós.
- Definir uma tabela de borda com um rótulo dinâmico ao lado de qualquer outra tabela de borda.
- Definir várias tabelas de nós ou de arestas que usam um rótulo dinâmico.
Por exemplo, o código a seguir falha ao tentar criar vários nós de gráfico com rótulos dinâmicos.
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 dos rótulos precisam estar em minúsculas
Você precisa armazenar valores de string de rótulo em letras minúsculas para correspondência. Recomendamos que você aplique essa regra no código do aplicativo ou usando restrições de esquema.
Embora os valores de string de rótulo precisem ser armazenados em letras minúsculas, eles não diferenciam maiúsculas de minúsculas quando são referenciados em uma consulta.
O exemplo a seguir mostra como inserir rótulos em valores minúsculos:
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
É possível usar rótulos que não diferenciam maiúsculas de minúsculas para corresponder ao GraphNode
ou GraphEdge
.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
Os nomes das propriedades precisam ser minúsculos
É preciso armazenar os nomes das propriedades em letras minúsculas. Recomendamos que você aplique essa regra no código do aplicativo ou usando restrições de esquema.
Embora os nomes de propriedades precisem ser armazenados em letras minúsculas, eles não diferenciam maiúsculas de minúsculas quando você os referencia na consulta.
O exemplo a seguir insere as propriedades name
e age
usando letras minúsculas.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
No texto da consulta, os nomes de propriedades não diferenciam maiúsculas de minúsculas. Por exemplo, é possível usar Age
ou age
para acessar a propriedade.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
Outras limitações
- O Spanner modela apenas chaves de nível superior do tipo de dados
JSON
como propriedades. - Os tipos de dados de propriedade precisam estar em conformidade com as especificações do tipo JSON do Spanner.
Práticas recomendadas para dados sem esquema
Esta seção descreve as práticas recomendadas que ajudam você a modelar dados sem esquema.
Definir chaves primárias para nós e arestas
A chave de um nó precisa ser única em todos os nós do gráfico. Por exemplo, como uma coluna INT64
ou string UUID
.
Se houver várias arestas entre dois nós, introduza um identificador exclusivo para a aresta. O exemplo de esquema usa uma coluna INT64
edge_id
de lógica de aplicativo.
Ao criar o esquema para tabelas de nós e arestas, inclua opcionalmente a coluna label
como uma coluna de chave primária se o valor for imutável. Se você fizer isso, a chave composta formada por todas as colunas de chave será exclusiva em todos os nós ou arestas. Essa técnica melhora a performance de consultas filtradas apenas por rótulo.
Para mais informações sobre a escolha da chave primária, consulte Escolher uma chave primária.
Criar um índice secundário para uma propriedade acessada com frequência
Para melhorar o desempenho de consultas em uma propriedade usada com frequência em filtros, crie um índice secundário em uma coluna de propriedade gerada. Em seguida, use-o em um esquema de gráfico e em consultas.
O exemplo a seguir mostra como adicionar uma coluna age
gerada à tabela GraphNode
para um nó person
. O valor é NULL
para nós sem o rótulo person
.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
A instrução DDL a seguir cria um NULL FILTERED INDEX
para person_age
e o intercala 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 disponíveis como propriedades de nó do gráfico. Para refletir isso na definição do gráfico de propriedades, use a instrução CREATE OR
REPLACE PROPERTY GRAPH
. Isso recompila a definição e inclui a nova coluna person_age
como uma propriedade.
Para mais informações, consulte Como atualizar definições de nós ou arestas.
A instrução a seguir 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 a seguir 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 mais recentes do
banco de dados.
Usar restrições de verificação para integridade de dados
O Spanner é compatível com objetos de esquema, como restrições de verificação, para garantir a integridade dos dados de rótulo e propriedade. Esta seção lista recomendações para restrições de verificação que podem ser usadas com dados sem esquema.
Aplicar valores de marcador
Recomendamos usar NOT NULL
na definição da coluna de marcador para evitar valores indefinidos.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
Aplicar valores de marcador e nomes de propriedades em letras minúsculas
Como os nomes de rótulos e propriedades precisam ser armazenados como valores minúsculos, faça uma destas ações:
- Aplique a verificação na lógica do aplicativo.
- Crie restrições de verificação no esquema.
No momento da consulta, o rótulo e o nome da propriedade não diferenciam maiúsculas de minúsculas.
O exemplo a seguir mostra como adicionar uma restrição de rótulo de nó à tabela
GraphNode
para garantir que o rótulo esteja em letras minúsculas.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
O exemplo a seguir mostra como adicionar uma restrição de verificação ao nome da propriedade
de aresta. A verificação usa
JSON_KEYS
para
acessar as chaves de nível superior.
COALESCE
converte a saída em uma matriz vazia se JSON_KEYS
retornar 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)));
Exigir que as propriedades existam
Crie uma restrição que verifique se uma propriedade existe para um rótulo.
No exemplo a seguir, 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));
Exigir propriedades exclusivas
Crie restrições baseadas em propriedades que verificam se a propriedade de um nó ou aresta é exclusiva em nós ou arestas com o mesmo rótulo. Para fazer isso, use um UNIQUE INDEX nas colunas geradas de propriedades.
No exemplo a seguir, 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 índice exclusivo
NULL_FILTERED
nas propriedadesPersonName
ePersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Aplicar tipo de dados da propriedade
Aplique um tipo de dados de propriedade usando uma restrição de tipo de dados em um valor de propriedade
para um rótulo, conforme mostrado no exemplo a seguir. Este exemplo usa a função
JSON_TYPE
para verificar se a propriedade name
do rótulo person
usa o
tipo STRING
.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
Combinar rótulos definidos e dinâmicos
O Spanner permite que os nós no gráfico de propriedades tenham rótulos definidos (no esquema) e dinâmicos (derivados de dados). Personalize os rótulos para usar essa flexibilidade.
Considere o esquema a seguir, 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, cada nó criado com base em GraphNode
tem o marcador definido Entity
. Além disso, cada nó tem um rótulo dinâmico determinado pelo valor na coluna de rótulo.
Em seguida, escreva consultas que correspondam a nós com base em qualquer tipo de rótulo. Por exemplo, a consulta a seguir encontra nós usando o rótulo Entity
definido:
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
Embora essa consulta use o rótulo definido Entity
, lembre-se de que o nó correspondente também tem um rótulo dinâmico com base nos dados dele.
Exemplos de esquema
Use os exemplos de esquema nesta seção como modelos para criar seus próprios esquemas. Os principais componentes do esquema incluem:
- Criação de tabelas de entrada de gráficos
- Criação de gráficos de propriedades
- Opcional: inverta o índice de travessia de arestas para aumentar o desempenho da travessia inversa.
- Opcional: indexar rótulos para melhorar a performance das consultas por rótulos
- Opcional: restrições de esquema para aplicar rótulos em letras minúsculas e nomes de propriedades
O exemplo a seguir 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 a seguir usa um índice para melhorar a travessia de arestas inversas. A cláusula
STORING (properties)
inclui uma cópia das propriedades de borda, o que acelera
consultas que filtram essas propriedades. Você pode omitir a cláusula STORING (properties)
se suas consultas não se beneficiarem dela.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
O exemplo a seguir usa um índice de rótulos para acelerar a correspondência de nós por rótulos.
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
O exemplo a seguir adiciona restrições que aplicam rótulos e propriedades em letras minúsculas. Os dois últimos exemplos usam a função
JSON_KEYS
. Opcionalmente, você pode aplicar a verificação de letras minúsculas na lógica do aplicativo.
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)));
Otimizar atualizações em lote de propriedades dinâmicas com DML
A modificação de propriedades dinâmicas usando funções como
JSON_SET
e
JSON_REMOVE
envolve operações de leitura/modificação/gravação. Isso pode resultar em um custo maior em comparação com a atualização de propriedades do tipo STRING
ou INT64
.
Se as cargas de trabalho envolverem atualizações em lote de propriedades dinâmicas usando DML, use as recomendações a seguir para melhorar o desempenho:
Atualize várias linhas em uma única instrução DML em vez de processar linhas individualmente.
Ao atualizar um intervalo de chaves amplo, agrupe e classifique as linhas afetadas por chaves primárias. Atualizar intervalos não sobrepostos com cada DML reduz a disputa de bloqueios.
Use parâmetros de consulta em instruções DML em vez de codificá-los para melhorar o desempenho.
Com base nessas sugestões, o exemplo a seguir mostra como atualizar a propriedade
is_blocked
para 100 nós em uma única instrução DML. Os parâmetros de consulta incluem o seguinte:
@node_ids
: chaves de linhasGraphNode
, armazenadas em um parâmetroARRAY
. Se aplicável, agrupar e classificar as DMLs melhora o desempenho.@is_blocked_values
: os valores correspondentes a serem atualizados, armazenados em um 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
Nesta seção, descrevemos como resolver problemas com dados sem esquema.
A propriedade aparece várias vezes no resultado TO_JSON
Problema
O nó a seguir modela as propriedades birthday
e name
como dinâmicas
na coluna JSON
. Propriedades birthday
e name
duplicadas
aparecem no resultado JSON do elemento de gráfico.
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
Isso retorna resultados semelhantes a estes:
{
…,
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 14,
"label": "person",
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex"
}
}
…
}
Possível causa
Por padrão, todas as colunas da tabela de base são definidas como propriedades. Usar
TO_JSON
ou
SAFE_TO_JSON
para retornar elementos do gráfico resulta em propriedades duplicadas. Isso acontece 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 esse comportamento, use a cláusula PROPERTIES ALL COLUMNS EXCEPT
para excluir a coluna properties
ao definir propriedades no esquema, conforme mostrado no exemplo a seguir:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Após a mudança de esquema, os elementos de gráfico retornados do tipo de dados JSON
não têm duplicatas.
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
Essa consulta retorna o seguinte:
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
Problemas comuns quando os valores de propriedade não são convertidos corretamente
Para corrigir os problemas a seguir, sempre use conversões de valor de propriedade ao usar uma propriedade em uma expressão de consulta.
Comparação de valores de propriedade sem conversão
Problema
No matching signature for operator = for argument types: JSON, STRING
Possível causa
A consulta não converte corretamente os valores das propriedades. Por exemplo, a propriedade name
não é convertida para o tipo STRING
na comparação:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Solução recomendada
Para corrigir esse problema, use uma conversão de valor antes da comparação.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
Isso retorna resultados semelhantes a estes:
+------+
| id |
+------+
| 1 |
+------+
Como alternativa, use um filtro de propriedade para simplificar as comparações de igualdade em que a conversão de valor ocorre automaticamente. O tipo do valor ("Alex") precisa corresponder exatamente ao tipo STRING
da propriedade em JSON
.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
Isso retorna resultados semelhantes a estes:
+------+
| 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
Possível causa
No exemplo a seguir, order_number_str
não foi convertido antes de ser
usado na instrução RETURN DISTINCT
:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
Solução recomendada
Para corrigir esse 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;
Isso retorna resultados semelhantes a estes:
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
Propriedade usada como uma chave de agrupamento sem conversão
Problema
Grouping by expressions of type JSON is not allowed.
Possível causa
No exemplo a seguir, 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 esse 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;
Isso retorna resultados semelhantes a estes:
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
Propriedade usada como uma chave de ordenação sem conversão
Problema
ORDER BY does not support expressions of type JSON
Possível causa
No exemplo a seguir, t.amount
não é convertido antes de ser usado para
ordenar 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 esse 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;
Isso retorna resultados semelhantes a estes:
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
Incompatibilidade de tipos durante a conversão
Problema
The provided JSON input is not an integer
Possível causa
No exemplo a seguir, a propriedade order_number_str
é armazenada como um tipo de dados JSON
STRING
. Se você tentar fazer uma conversão para INT64
, um erro será retornado.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Solução recomendada
Para corrigir esse problema, use o conversor de valor exato que corresponde ao tipo de valor.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
Isso retorna resultados semelhantes a estes:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Como alternativa, use um conversor flexível quando o valor puder ser convertido para o tipo de destino, conforme mostrado no exemplo a seguir:
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Isso retorna resultados semelhantes a estes:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
A seguir
- Para saber mais sobre JSON, consulte Modificar dados JSON e Lista de funções JSON.
- Compare o Spanner Graph e o openCypher.
- Migre para o Spanner Graph.