En esta página, se describe cómo administrar datos sin esquema en Spanner Graph. También se detallan las prácticas recomendadas y las sugerencias para solucionar problemas. Se recomienda tener conocimientos sobre el esquema y las consultas de Spanner Graph.
La administración de datos sin esquema te permite crear una definición flexible de un grafo, en la que las definiciones de tipo de nodo y arista se pueden agregar, actualizar o borrar sin cambios en el esquema. Este enfoque admite el desarrollo iterativo y una menor sobrecarga de administración de esquemas, al mismo tiempo que conserva la experiencia familiar de las consultas de gráficos.
La administración de datos sin esquema es particularmente útil en las siguientes situaciones:
- Administras gráficos con cambios frecuentes, como actualizaciones y adiciones de etiquetas y propiedades de elementos.
- Tu gráfico tiene muchos tipos de nodos y aristas, lo que dificulta la creación y administración de las tablas de entrada.
Cómo modelar datos sin esquema
Spanner Graph te permite crear un gráfico a partir de tablas en las que las filas se asignan a nodos y aristas. En lugar de usar tablas separadas para cada tipo de elemento, el modelado de datos sin esquema suele emplear una sola tabla de nodos y una sola tabla de aristas con una columna STRING
para la etiqueta y una columna JSON
para las propiedades.
Crea tablas de entrada
Puedes crear una sola tabla GraphNode
y una sola tabla GraphEdge
para almacenar datos sin esquema, como se muestra en el siguiente ejemplo. Los nombres de las tablas se incluyen solo a modo de ejemplo, y puedes elegir los que quieras.
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;
En este ejemplo, se realizan las acciones siguientes:
Almacena todos los nodos en una sola tabla
GraphNode
, identificados de forma única por elid
.Almacena todos los bordes en una sola tabla
GraphEdge
, identificados de forma única por la combinación de origen (id
), destino (dest_id
) y su propio identificador (edge_id
). Se incluye unedge_id
como parte de la clave primaria para permitir más de un borde de un parid
a un pardest_id
.
Tanto la tabla de nodos como la de aristas tienen sus propias columnas label
y properties
de tipo STRING
y JSON
, respectivamente.
Crea un gráfico de propiedades
Con la instrucción CREATE PROPERTY GRAPH, las tablas de entrada de la sección anterior se asignan como nodos y aristas. Debes usar las siguientes cláusulas para definir etiquetas y propiedades para los datos sin esquema:
DYNAMIC LABEL
: Crea la etiqueta de un nodo o una arista a partir de una columnaSTRING
de la tabla de entrada.DYNAMIC PROPERTIES
: Crea propiedades de un nodo o un borde a partir de una columnaJSON
de la tabla de entrada.
En el siguiente ejemplo, se muestra cómo crear un gráfico con esas 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)
);
Etiqueta dinámica
La cláusula DYNAMIC LABEL
designa una columna de tipo de datos STRING
para almacenar los valores de la etiqueta.
Por ejemplo, en una fila GraphNode
, si la columna label
tiene un valor person
, se asigna a un nodo Person
dentro del gráfico. Del mismo modo, en una fila GraphEdge
, si la columna de etiquetas tiene un valor de owns
, se asigna a una arista Owns
dentro del gráfico.
Propiedades dinámicas
La cláusula DYNAMIC PROPERTIES
designa una columna de tipo de datos JSON
para almacenar propiedades. Las claves JSON son los nombres de las propiedades y los valores JSON son los valores de las propiedades.
Por ejemplo, cuando la columna properties
de una fila GraphNode
tiene el valor JSON '{"name": "David", "age": 43}'
, se asigna a un nodo con las propiedades age
y name
, con 43
y "David"
como valores de propiedad.
Cuándo no usar la administración de datos sin esquema
Es posible que no desees usar la administración de datos sin esquema en las siguientes situaciones:
- Los tipos de nodos y aristas de los datos de tu gráfico están bien definidos, o bien sus etiquetas y propiedades no necesitan actualizaciones frecuentes.
- Tus datos ya están almacenados en Spanner y prefieres compilar gráficos a partir de tablas existentes en lugar de introducir tablas de nodos y bordes nuevas y dedicadas.
- Las limitaciones de los datos sin esquema impiden tu adopción.
Además, si tu carga de trabajo es muy sensible al rendimiento de escritura, en especial cuando las propiedades se actualizan con frecuencia, usar propiedades definidas por el esquema con tipos de datos primitivos como STRING
o INT64
es más eficaz que usar propiedades dinámicas con el tipo JSON
.
Para obtener más información sobre cómo definir el esquema del gráfico sin usar etiquetas y propiedades de datos dinámicos, consulta la descripción general del esquema de Spanner Graph.
Consulta datos de gráficos sin esquema
Puedes consultar datos de gráficos sin esquema con Graph Query Language (GQL). Puedes usar las consultas de muestra en la Descripción general de las consultas de Spanner Graph y la Referencia de GQL con modificaciones limitadas.
Cómo hacer coincidir nodos y aristas con etiquetas
Puedes hacer coincidir nodos y aristas con la expresión de etiqueta en GQL.
La siguiente consulta coincide con los nodos y los bordes conectados que tienen los valores account
y transfers
en su columna de etiquetas.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Propiedades de acceso
Las claves y los valores de nivel superior del tipo de datos JSON
se modelan como propiedades, como age
y name
en el siguiente ejemplo.
JSON document |
Properties |
|
|
|
En el siguiente ejemplo, se muestra cómo acceder a la propiedad name
desde el nodo Person
.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
Esto devuelve resultados similares a los siguientes:
JSON"Tom"
Cómo convertir tipos de datos de propiedades
Las propiedades se tratan como valores del tipo de datos JSON. En algunos casos, como para las comparaciones con tipos de SQL, primero deben convertirse a un tipo de SQL.
En el siguiente ejemplo, la consulta realiza las siguientes conversiones de tipo de datos:
- Convierte la propiedad
is_blocked
en un tipo booleano para evaluar la expresión. - Convierte la propiedad
order_number_str
en un tipo de cadena y la compara con el valor literal"302290001255747"
. - Usa la función LAX_INT64 para convertir de forma segura
order_number_str
en un número entero como el tipo de datos que se devuelve.
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;
Esto devuelve resultados similares a los siguientes:
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
En cláusulas como GROUP BY
y ORDER BY
, también debes convertir el tipo de datos JSON. En el siguiente ejemplo, se convierte la propiedad city
a un tipo de cadena, lo que permite usarla para agrupar.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
Sugerencias para convertir tipos de datos JSON en tipos de datos SQL:
- Los convertidores estrictos, como
INT64
, realizan verificaciones rigurosas de tipos y valores. Esto se recomienda cuando se conoce y se aplica el tipo de datos JSON, por ejemplo, cuando se usan restricciones de esquema para aplicar el tipo de datos de la propiedad. - Los convertidores flexibles, como
LAX_INT64
, convierten el valor de forma segura cuando es posible y devuelvenNULL
cuando la conversión no es factible. Se recomienda cuando no se requiere una verificación rigurosa o los tipos son difíciles de aplicar.
Puedes obtener más información sobre la conversión de datos en las sugerencias para solucionar problemas.
Cómo filtrar por valores de propiedad
En los filtros de propiedad, los parámetros del filtro se tratan como valores del tipo de datos JSON
. Por ejemplo, en la siguiente consulta, is_blocked
se trata como un boolean
JSON y order_number_str
como un string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
Esto devuelve resultados similares a los siguientes:
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
El parámetro de filtro debe coincidir con el tipo y el valor de la propiedad. Por ejemplo, cuando el parámetro de filtro order_number_str
es un número entero, no se encuentra ninguna coincidencia, ya que la propiedad es un string
de JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Accede a propiedades JSON anidadas
Las claves y los valores JSON anidados no se modelan como propiedades. En el siguiente ejemplo, las claves JSON city
, state
y country
no se modelan como propiedades porque están anidadas en location
. Sin embargo, puedes acceder a ellos con un operador de acceso a campos o un operador de subíndice de JSON.
JSON document |
Properties |
|
|
|
|
|
En el siguiente ejemplo, se muestra cómo acceder a las propiedades anidadas con el operador de acceso del campo JSON.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
Esto devuelve resultados similares a los siguientes:
"New York"
Modifica datos sin esquema
Spanner Graph asigna datos de tablas a nodos y vínculos de gráficos. Cuando cambias los datos de la tabla de entrada, se producen mutaciones directamente en los datos del gráfico correspondientes. Para obtener más información sobre la mutación de datos de gráficos, consulta Cómo insertar, actualizar o borrar datos de gráficos de Spanner.
Ejemplos
En esta sección, se proporcionan ejemplos de cómo crear, actualizar y borrar datos de grafos.
Cómo insertar datos de gráficos
En el siguiente ejemplo, se inserta un nodo person
. Las etiquetas y los nombres de las propiedades deben usar letras minúsculas.
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
Actualiza los datos del gráfico
En el siguiente ejemplo, se actualiza un nodo Account
y se usa la función JSON_SET
para establecer su propiedad is_blocked
.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
En el siguiente ejemplo, se actualiza un nodo person
con un nuevo conjunto de propiedades.
UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;
En el siguiente ejemplo, se usa la función JSON_REMOVE
para quitar la propiedad is_blocked
de un nodo Account
. Después de la ejecución, todas las demás propiedades existentes permanecen sin cambios.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
Borra los datos del gráfico
En el siguiente ejemplo, se borra la arista Transfers
en los nodos Account
que se transfirieron a cuentas 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
}
Limitaciones
En esta sección, se enumeran las limitaciones del uso de la administración de datos sin esquema.
Requisito de una sola tabla para la etiqueta dinámica
Solo puedes tener una tabla de nodos si se usa una etiqueta dinámica en su definición. Esta restricción también se aplica a la tabla de borde. No se permite lo siguiente:
- Definir una tabla de nodos con una etiqueta dinámica junto con cualquier otra tabla de nodos
- Definir una tabla de borde con una etiqueta dinámica junto con cualquier otra tabla de borde
- Definir varias tablas de nodos o varias tablas de aristas que usen una etiqueta dinámica
Por ejemplo, el siguiente código falla cuando intenta crear varios nodos de gráfico con 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 (
...
);
Los nombres de las etiquetas deben estar en minúsculas.
Los valores de cadena de la etiqueta deben almacenarse en minúsculas para que coincidan. Te recomendamos que apliques esta regla en el código de la aplicación o con restricciones de esquema.
Si bien los valores de cadena de etiquetas deben almacenarse en minúsculas, no distinguen mayúsculas de minúsculas cuando se hace referencia a ellos en una búsqueda.
En el siguiente ejemplo, se muestra cómo insertar etiquetas en valores en minúscula:
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
Puedes usar etiquetas que no distinguen mayúsculas de minúsculas para que coincidan con GraphNode
o GraphEdge
.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
Los nombres de las propiedades deben estar en minúsculas
Los nombres de las propiedades deben almacenarse en minúsculas. Te recomendamos que apliques esta regla en el código de la aplicación o con restricciones de esquema.
Si bien los nombres de las propiedades deben almacenarse en minúsculas, no distinguen mayúsculas de minúsculas cuando los referencias en tu consulta.
En el siguiente ejemplo, se insertan las propiedades name
y age
con letras minúsculas.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
En el texto de la consulta, los nombres de las propiedades no distinguen mayúsculas de minúsculas. Por ejemplo, puedes usar Age
o age
para acceder a la propiedad.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
Otras limitaciones
- Solo las claves de nivel superior del tipo de datos
JSON
se modelan como propiedades. - Los tipos de datos de propiedad deben cumplir con las especificaciones del tipo JSON de Spanner.
Prácticas recomendadas
En esta sección, se describen las prácticas recomendadas para modelar datos sin esquema.
Definiciones de clave primaria para nodos y aristas
La clave de un nodo debe ser única en todos los nodos del gráfico. Por ejemplo, como una columna de INT64
o de cadena UUID
.
Si tienes varias aristas entre dos nodos, debes introducir un identificador único para la arista. El ejemplo de esquema usa una columna INT64
edge_id
de lógica de la aplicación.
Cuando creas el esquema para las tablas de nodos y aristas, puedes incluir de forma opcional la columna label
como una columna de clave primaria, si el valor es inmutable. Si lo haces, la clave compuesta formada por todas las columnas de clave debe ser única en todos los nodos o aristas. Esta técnica mejora el rendimiento de las consultas que solo se filtran por etiqueta.
Para obtener más información sobre la elección de la clave primaria, consulta Cómo elegir una clave primaria.
Índice secundario para una propiedad a la que se accede con frecuencia
Para mejorar el rendimiento de las consultas de una propiedad que se usa con frecuencia en los filtros, puedes crear un índice secundario en una columna de propiedad generada y, luego, usarlo en el esquema y las consultas del gráfico.
En el siguiente ejemplo, se agrega una columna age
generada a la tabla GraphNode
para un nodo person
. El valor es NULL
para los nodos sin la etiqueta person
.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
Luego, crea un NULL FILTERED INDEX
para person_age
y lo intercala en la tabla GraphNode
para el acceso local.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
La tabla GraphNode
ahora incluye columnas nuevas que están disponibles como propiedades de nodos del gráfico. Para reflejar esto en la definición del gráfico de propiedades, usa la instrucción CREATE OR REPLACE PROPERTY GRAPH
. Esto vuelve a compilar la definición y, luego, incluye la nueva columna person_age
como una propiedad.
La siguiente instrucción vuelve a compilar la definición y, además, incluye la nueva columna person_age
como una propiedad.
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)
);
En el siguiente ejemplo, se ejecuta una consulta con la propiedad indexada.
GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;
De manera opcional, puedes ejecutar el comando ANALYZE
después de crear el índice para que el optimizador de consultas se actualice con las estadísticas de la base de datos más recientes.
Restricciones de verificación para la integridad de los datos
Spanner admite objetos de esquema, como restricciones de verificación, para aplicar la integridad de los datos de etiquetas y propiedades. En esta sección, se enumeran recomendaciones para las restricciones de verificación que puedes usar con datos sin esquema.
Aplica valores de etiqueta válidos
Te recomendamos que uses NOT NULL
en la definición de la columna 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);
Aplicar los valores de etiqueta y los nombres de propiedad en minúsculas
Dado que los nombres de etiquetas y propiedades deben almacenarse como valores en minúscula, te recomendamos que hagas una de las siguientes acciones:
- Aplica la verificación en la lógica de tu aplicación.
- Crea restricciones de verificación en el esquema.
En el momento de la consulta, la etiqueta y el nombre de la propiedad no distinguen mayúsculas de minúsculas.
En el siguiente ejemplo, se agrega una restricción de etiqueta de nodo a la tabla GraphNode
para garantizar que la etiqueta esté en minúsculas.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
En el siguiente ejemplo, se agrega una restricción de verificación al nombre de la propiedad de borde. La verificación usa JSON_KEYS
para acceder a las claves de nivel superior. COALESCE
convierte el resultado en un array vacío si JSON_KEYS
devuelve NULL
y, luego, verifica que cada clave esté en minúsculas.
ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
Aplicar la existencia de la propiedad
Puedes crear una restricción que verifique si existe una propiedad para una etiqueta.
En el siguiente ejemplo, la restricción verifica si un nodo person
tiene una propiedad name
.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
Cómo aplicar la unicidad de la propiedad
Puedes crear restricciones basadas en propiedades que verifiquen si la propiedad de un nodo o borde es única en todos los nodos o bordes con la misma etiqueta. Para ello, usa un ÍNDICE ÚNICO en las columnas generadas de las propiedades.
En el siguiente ejemplo, el índice único verifica que las propiedades name
y country
combinadas sean únicas para cualquier nodo person
.
Agrega una columna generada para
PersonName
.ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
Agrega una columna generada para
PersonCountry
.ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
Crea un índice único
NULL_FILTERED
en las propiedadesPersonName
yPersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Aplica tipos de datos de propiedad
Puedes aplicar un tipo de datos de propiedad con una restricción de tipo de datos en un valor de propiedad para una etiqueta, como se muestra en el siguiente ejemplo. En este ejemplo, se usa la función JSON_TYPE
para verificar que la propiedad name
de la etiqueta person
use el tipo STRING
.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
Combinación de etiquetas definidas y dinámicas
Spanner permite que los nodos de tu gráfico de propiedades tengan etiquetas definidas (en el esquema) y etiquetas dinámicas (derivadas de los datos). Puedes personalizar las etiquetas para aprovechar esta flexibilidad.
Considera el siguiente esquema que muestra la creación de la tabla 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)
);
Aquí, cada nodo creado a partir de GraphNode
tiene la etiqueta definida Entity
. Además, cada nodo tiene una etiqueta dinámica determinada por el valor de su columna de etiquetas.
Luego, puedes escribir consultas que coincidan con los nodos según el tipo de etiqueta. Por ejemplo, la siguiente consulta busca nodos con la etiqueta Entity
definida:
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
Aunque esta consulta usa la etiqueta definida Entity
, recuerda que el nodo coincidente también incluye una etiqueta dinámica basada en sus datos.
Ejemplos de esquemas
Puedes usar los ejemplos de esquemas de esta sección como plantillas para crear tus propios esquemas. Entre los componentes clave del esquema, se incluyen los siguientes:
- Creación de tablas de entrada de gráficos
- Creación de gráficos de propiedades
- Opcional: Índice de recorrido de borde inverso para mejorar el rendimiento del recorrido inverso
- Opcional: Etiqueta de índice para mejorar el rendimiento de las búsquedas por etiquetas
- Opcional: Restricciones de esquema para aplicar etiquetas en minúsculas y nombres de propiedades
En el siguiente ejemplo, se muestra cómo crear tablas de entrada y un gráfico de propiedades:
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)
);
En el siguiente ejemplo, se usa un índice para mejorar el recorrido de los bordes inversos. La cláusula STORING (properties)
incluye una copia de las propiedades de borde, lo que acelera las consultas que filtran en función de estas propiedades. Puedes omitir la cláusula STORING (properties)
si tus consultas no se benefician de ella.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
En el siguiente ejemplo, se usa un índice de etiquetas para acelerar la coincidencia de nodos por etiquetas.
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
En el siguiente ejemplo, se agregan restricciones que aplican etiquetas y propiedades en minúsculas. En los últimos dos ejemplos, se usa la función JSON_KEYS
. De manera opcional, puedes aplicar la verificación de letras minúsculas en la lógica de la aplicación.
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)));
Cómo optimizar las actualizaciones por lotes de propiedades dinámicas con DML
Modificar propiedades dinámicas con funciones como JSON_SET
y JSON_REMOVE
implica operaciones de lectura-modificación-escritura. Pueden generar un costo más alto en comparación con la actualización de las propiedades de tipo STRING
o INT64
.
Si tus cargas de trabajo implican actualizaciones por lotes de propiedades dinámicas con DML, considera las siguientes recomendaciones para lograr un mejor rendimiento:
Actualiza varias filas en una sola declaración DML en lugar de procesar las filas de forma individual.
Cuando actualices un rango de claves amplio, agrupa y ordena las filas afectadas por sus claves principales. La actualización de rangos que no se superponen con cada DML reduce la contención de bloqueos.
Usa parámetros de consulta en las instrucciones de DML en lugar de codificarlos de forma rígida para mejorar el rendimiento.
Según estas sugerencias, en el siguiente ejemplo, se actualiza la propiedad is_blocked
para 100 nodos en una sola declaración DML. Los parámetros de consulta incluyen lo siguiente:
@node_ids
: Son las claves de las filas deGraphNode
, almacenadas en un parámetroARRAY
. Si corresponde, agruparlos y ordenarlos en todos los DML permite lograr un mejor rendimiento.@is_blocked_values
: Son los valores correspondientes que se actualizarán y que se almacenan en un 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)
Solucionar problemas
En esta sección, se describe cómo solucionar problemas relacionados con los datos sin esquema.
La propiedad aparece más de una vez en el resultado de TO_JSON
Problema
El siguiente nodo modela las propiedades birthday
y name
como propiedades dinámicas en su columna JSON
. Las propiedades duplicadas de birthday
y name
aparecen en el resultado JSON del elemento del gráfico.
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
Esto devuelve resultados similares a los siguientes:
{
…,
"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 posible
De forma predeterminada, todas las columnas de la tabla base se definen como propiedades. Usar TO_JSON
o SAFE_TO_JSON
para devolver elementos del gráfico genera propiedades duplicadas. Esto se debe a que la columna JSON
(es decir, properties
) es una propiedad definida por el esquema, mientras que las claves de primer nivel de JSON
se modelan como propiedades dinámicas.
Solución recomendada
Para evitar este comportamiento, usa la cláusula PROPERTIES ALL COLUMNS EXCEPT
para excluir la columna properties
cuando definas propiedades en el esquema, como se muestra en el siguiente ejemplo:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Después del cambio de esquema, los elementos del gráfico devueltos del tipo de datos JSON
no tienen duplicados.
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
Esta consulta devuelve lo siguiente:
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
Problemas habituales cuando los valores de propiedad no se convierten correctamente
Una solución común para los siguientes problemas es usar siempre conversiones de valores de propiedad cuando se usa una propiedad dentro de una expresión de consulta.
Comparación de valores de propiedad sin conversión
Problema
No matching signature for operator = for argument types: JSON, STRING
Causa posible
La consulta no convierte correctamente los valores de propiedad. Por ejemplo, la propiedad name
no se convierte al tipo STRING
en la comparación:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Solución recomendada
Para solucionar este problema, usa una conversión de valor antes de la comparación.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
Esto devuelve resultados similares a los siguientes:
+------+
| id |
+------+
| 1 |
+------+
Como alternativa, usa un filtro de propiedad para simplificar las comparaciones de igualdad en las que la conversión de valores se realiza automáticamente. Ten en cuenta que el tipo del valor ("Alex") debe coincidir exactamente con el tipo STRING
de la propiedad en JSON
.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
Esto devuelve resultados similares a los siguientes:
+------+
| id |
+------+
| 1 |
+------+
Uso del valor de la propiedad RETURN DISTINCT
sin conversión
Problema
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
Causa posible
En el siguiente ejemplo, order_number_str
no se convirtió antes de usarse en la sentencia RETURN DISTINCT
:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
Solución recomendada
Para solucionar este problema, usa una conversión de valores antes de RETURN DISTINCT
.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
Esto devuelve resultados similares a los siguientes:
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
Propiedad utilizada como clave de agrupación sin conversión
Problema
Grouping by expressions of type JSON is not allowed.
Causa posible
En el siguiente ejemplo, t.order_number_str
no se convierte antes de usarse para agrupar objetos JSON:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
Solución recomendada
Para solucionar este problema, usa una conversión de valores antes de usar la propiedad como clave de agrupación.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
Esto devuelve resultados similares a los siguientes:
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
Propiedad utilizada como clave de ordenamiento sin conversión
Problema
ORDER BY does not support expressions of type JSON
Causa posible
En el siguiente ejemplo, t.amount
no se convierte antes de usarse para ordenar los 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;
Solución recomendada
Para solucionar este problema, realiza una conversión en t.amount
en la 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;
Esto devuelve resultados similares a los siguientes:
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
No coincide el tipo durante la conversión
Problema
The provided JSON input is not an integer
Causa posible
En el siguiente ejemplo, la propiedad order_number_str
se almacena como un tipo de datos STRING
JSON. Si intentas realizar una conversión a INT64
, se muestra un error.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Solución recomendada
Para solucionar este problema, usa el convertidor de valores exactos que coincida con el tipo de valor.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
Esto devuelve resultados similares a los siguientes:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Como alternativa, usa un convertidor flexible cuando el valor se pueda convertir al tipo de destino, como se muestra en el siguiente ejemplo:
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Esto devuelve resultados similares a los siguientes:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
¿Qué sigue?
- Para obtener más información sobre JSON, consulta Modifica datos de JSON y la lista de funciones de JSON.
- Compara Spanner Graph y openCypher.
- Migra a Spanner Graph.