Crea un gráfico a partir de una vista de SQL

Aprende a crear un grafo con vistas de SQL. En este documento, se proporcionan instrucciones paso a paso y ejemplos de código para definir vistas y usarlas para definir tablas de nodos y bordes. Explora ejemplos con código de muestra que ilustran casos de uso para crear gráficos con vistas. Para obtener más información sobre el uso de vistas para crear un grafo de propiedades, incluidos los beneficios y las consideraciones, consulta Descripción general de los grafos creados a partir de vistas de SQL.

Antes de comenzar

Para crear un grafo, debes hacer lo siguiente:

  1. Asegúrate de que tu entorno de Spanner Graph esté configurado.

  2. Familiarízate con cómo funcionan los esquemas de Spanner Graph.

Crea un gráfico con vistas

Para crear un gráfico con vistas, haz lo siguiente:

  1. Define vistas para tu gráfico. De forma predeterminada, tus vistas deben seguir uno de los patrones de vista admitidos para garantizar la unicidad de los elementos. Si tus vistas usan consultas que no siguen estos patrones, puedes inhabilitar la validación de unicidad de claves en el paso 4. Para obtener más información, consulta Crea una vista.

  2. Usa tus vistas en las cláusulas NODE TABLES y EDGE TABLES de la sentencia CREATE PROPERTY GRAPH para crear un grafo.

  3. Incluye la cláusula KEY en la sentencia CREATE PROPERTY GRAPH. La cláusula KEY especifica las columnas de la vista de origen que identifican de forma inequívoca cada elemento del grafo.

  4. Opcional: Si tus vistas usan consultas en SQL arbitrarias que no siguen los patrones de unicidad admitidos, agrega la cláusula OPTIONS (validate_element_key_uniqueness = false) a la declaración CREATE PROPERTY GRAPH. Asegúrate de que las columnas de la cláusula KEY produzcan valores únicos para cada fila de nodo o de borde:

    CREATE PROPERTY GRAPH MyGraph
      NODE TABLES (
        CustomerViewTrusted KEY(id)
      ) OPTIONS (validate_element_key_uniqueness = false);
    

    Para obtener más información, consulta Validación de claves inhabilitadas (cualquier consulta en SQL).

  5. Opcional: Si tus vistas llaman a una función remota o tienen otras columnas volátiles, agrega la cláusula OPTIONS (allow_view_with_volatile_column = true) a la sentencia CREATE PROPERTY GRAPH:

    CREATE PROPERTY GRAPH MyGraph
      NODE TABLES (
        AccountRemoteUdfView KEY(account_id)
      ) OPTIONS (allow_view_with_volatile_column = true);
    

    Para obtener más información, consulta Habilita columnas volátiles cuando las vistas usen funciones remotas.

Ejemplo: Crea un grafo con vistas

En este ejemplo, se crean las siguientes vistas en las tablas Customer y Account: AsiaCustomer, AsiaBankAccount y AsiaAccountsOwnership. Luego, en el ejemplo, se usan estas vistas para crear lo siguiente en un gráfico:

  • Crea la tabla de nodos Customer con la vista AsiaCustomer.

  • Crea la tabla de nodos Account con la vista AsiaBankAccount.

  • Crea la tabla perimetral Owns con la vista AsiaAccountsOwnership. Este borde conecta los nodos Customer con los nodos Account.

Paso 1: Crea las tablas

Primero, crea las tablas de datos. Con el siguiente código, se crean las tablas Customer y Account.

CREATE TABLE Customer (
  customer_id INT64 NOT NULL,
  name STRING(MAX),
  address_continent STRING(MAX),
  address_country STRING(MAX),
) PRIMARY KEY(customer_id);

CREATE TABLE Account (
  account_id INT64 NOT NULL,
  customer_id INT64 NOT NULL,
  account_type STRING(MAX),
  balance INT64,
  create_time TIMESTAMP,
  address_continent STRING(MAX),
  address_country STRING(MAX),
  CONSTRAINT FK_CustomerId FOREIGN KEY (customer_id)
    REFERENCES Customer (customer_id)
) PRIMARY KEY(account_id);

Paso 2: Crea las vistas

Luego, crea vistas para transformar o filtrar datos de las tablas. Estas vistas filtran las tablas para incluir solo clientes y cuentas de Asia. A menos que validate_element_key_uniqueness se establezca en false, las vistas que se usan para crear elementos del grafo deben seguir patrones admitidos para garantizar que las filas de la vista sean únicas.

-- View for 'Customer' nodes, filtered for Asia
CREATE VIEW AsiaCustomer
  SQL SECURITY INVOKER AS
    SELECT customer.customer_id, customer.name
    FROM Customer customer
    WHERE LOWER(customer.address_continent) = "asia";

-- View for 'Account' nodes, filtered for Asia.
CREATE VIEW AsiaBankAccount
  SQL SECURITY INVOKER AS
    SELECT account.account_id, account.balance, account.account_type, account.create_time
    FROM Account account
    WHERE LOWER(account.address_continent) = "asia";

-- View for 'Owns' edges, connecting customers to accounts in Asia.
CREATE VIEW AsiaAccountsOwnership
  SQL SECURITY INVOKER AS
    SELECT account.customer_id, account.account_id
    FROM Account account
    WHERE LOWER(account.address_continent) = "asia";

Paso 3: Crea el grafo de propiedades

Ahora, crea el AsiaFinGraph con las vistas que creaste. La sentencia CREATE PROPERTY GRAPH incluye la cláusula KEY para cada definición de elemento del grafo para especificar las columnas que identifican de forma inequívoca los elementos del grafo.

CREATE PROPERTY GRAPH AsiaFinGraph
  NODE TABLES (
    AsiaCustomer AS Customer KEY(customer_id),
    AsiaBankAccount AS Account KEY(account_id)
  )
  EDGE TABLES (
    AsiaAccountsOwnership AS Owns
      KEY(customer_id, account_id)
      SOURCE KEY (customer_id) REFERENCES Customer (customer_id)
      DESTINATION KEY (account_id) REFERENCES Account (account_id)
  );

Ejemplos de casos de uso

Las vistas de SQL ofrecen beneficios en comparación con el uso de tablas para los elementos de grafos de propiedades. En los siguientes ejemplos, se muestran algunos casos de uso para definir elementos de grafos con vistas en lugar de tablas.

Ejemplo: Aplica un control de acceso detallado a los datos de grafos

Para aplicar la seguridad a nivel de fila en tus datos de grafo, define tus tablas de nodos o bordes con las vistas de derechos del definidor. La vista expone un subconjunto permitido de los datos subyacentes al gráfico.

Por ejemplo, para restringir el acceso al grafo solo a los empleados de un centro de costos de ingeniería, puedes crear una vista EngineerEmployeeView y otorgar permisos SELECT en la vista a un rol engineering_data_reader con la cláusula GRANT.

Cuando defines una tabla de nodos de grafo con esta vista, los usuarios que ejecutan consultas de grafo con el rol engineering_data_reader solo pueden ver las filas filtradas por la vista, que incluyen a los empleados de ingeniería.

-- The table containing all employee data.
CREATE TABLE Employee (
  id INT64 NOT NULL,
  cost_center STRING(MAX),
  job_title STRING(MAX),
  office STRING(MAX)
) PRIMARY KEY (id);

-- The definer's rights view that filters for engineering employees.
CREATE VIEW EngineerEmployeeView SQL SECURITY DEFINER AS
  SELECT e.id, e.cost_center, e.job_title, e.office
  FROM Employee e
  WHERE LOWER(e.cost_center) = "engineering";

-- The role that is granted to read the view.
CREATE ROLE engineering_data_reader;
GRANT SELECT ON VIEW EngineerEmployeeView TO ROLE engineering_data_reader;

-- The graph that uses definer's rights view.
CREATE PROPERTY GRAPH EngineeringGraph
  NODE TABLES (
    EngineerEmployeeView KEY(id)
  );

Ejemplo: Elementos del grafo derivados del modelo

Puedes usar vistas para definir elementos de gráficos que requieran transformaciones de datos. Un beneficio clave es que la vista define la transformación, por lo que no necesitas mantener una tabla independiente para los datos derivados.

Anula el anidamiento de arrays para modelar aristas

Por ejemplo, puedes usar UNNEST en los datos de una columna ARRAY (o un campo de array dentro de una columna JSON) para modelar varias relaciones de borde desde una sola fila.

En el siguiente ejemplo de esquema de cadena de suministro, una tabla Parts almacena una lista de subcomponentes en un array dependent_parts. Una vista puede usar el operador UNNEST para transformar cada elemento de ese array en filas distintas. Esta vista puede servir como una tabla de aristas, lo que te permite modelar una arista PartDependsOnPart para representar las relaciones de dependencia entre las partes.

-- Parts table with an ARRAY of dependent parts.
CREATE TABLE Parts (
  part_id INT64 NOT NULL,
  dependent_parts ARRAY<INT64>
) PRIMARY KEY (part_id);

-- A view that unnests the dependent_parts array.
-- GROUP BY ensures uniqueness for the graph element KEY.
CREATE VIEW PartDependsOnPart SQL SECURITY INVOKER AS
  SELECT p.part_id, dependent_part_id
  FROM Parts AS p,
    UNNEST(p.dependent_parts) AS dependent_part_id
  GROUP BY p.part_id, dependent_part_id;

-- Graph modeling the part dependency relationship.
CREATE PROPERTY GRAPH SupplyChainGraph
  NODE TABLES (
    Parts
  )
  EDGE TABLES (
    PartDependsOnPart KEY (part_id, dependent_part_id)
      SOURCE KEY (part_id) REFERENCES Parts(part_id)
      DESTINATION KEY (dependent_part_id) REFERENCES Parts(part_id)
  );

Calcula propiedades con funciones remotas

También puedes usar una función remota en una vista para calcular las propiedades derivadas de los elementos del grafo estableciendo allow_view_with_volatile_column = true en la cláusula OPTIONS de la sentencia CREATE PROPERTY GRAPH. Para obtener más información, consulta Habilita columnas volátiles cuando las vistas usen funciones remotas.

-- Define a remote function.
CREATE FUNCTION rudf_identity_func(x INT64)
  RETURNS INT64 NOT DETERMINISTIC LANGUAGE REMOTE
  OPTIONS (
    endpoint = 'https://us-central1-project-id.cloudfunctions.net/my-func'
  );

-- Base table for accounts.
CREATE TABLE Account (
  account_id INT64 NOT NULL,
  balance INT64
) PRIMARY KEY (account_id);

-- View that computes a property using the remote function.
CREATE VIEW AccountRemoteUdfView SQL SECURITY INVOKER AS
  SELECT
    t.account_id,
    t.balance,
    rudf_identity_func(t.account_id) AS view_computed_prop
  FROM Account AS t;

-- Graph using the view requires allow_view_with_volatile_column = true.
CREATE PROPERTY GRAPH AccountViewRemoteUdfGraph
  NODE TABLES (
    AccountRemoteUdfView KEY (account_id)
      PROPERTIES (account_id, balance, view_computed_prop)
  )
  OPTIONS (allow_view_with_volatile_column = true);

Ejemplo: Transición de datos sin esquema

La administración de datos sin esquema te permite crear una definición de grafo flexible sin tipos de nodos y bordes predefinidos. Si bien la administración de datos sin esquema proporciona flexibilidad, es posible que debas realizar la transición a una estructura más formal a medida que tus datos se definan mejor. Una estructura más formal expone las relaciones de nodos y aristas, las etiquetas y las propiedades del grafo en el esquema, lo que reduce la necesidad de explorar los datos de forma manual para comprender el esquema del grafo.

Puedes usar vistas para formalizar los tipos de nodos y aristas sin migrar tus datos subyacentes. Por ejemplo, puedes realizar la transición de un modelo típico sin esquema que use tablas canónicas GraphNode y GraphEdge. Para ello, crea vistas que extraigan los datos de tus tablas sin esquema:

  1. Define una vista para cada tipo de nodo y de borde que quieras formalizar (por ejemplo, Person o WorksFor). En la vista, filtra los datos por su etiqueta (por ejemplo, WHERE n_label = "person") y convierte las propiedades de la columna JSON en tipos de datos específicos (por ejemplo, STRING(prop.name) AS name).

  2. Define un nuevo gráfico de propiedades en el que NODE TABLES y EDGE TABLES hagan referencia a las vistas con tipo que acabas de crear.

Un grafo sin esquema proporciona un mejor rendimiento que un grafo formalizado para algunas consultas (por ejemplo, un patrón de ruta cuantificado con varios tipos de bordes). Si los metadatos formalizados son importantes para tu caso de uso, puedes usar vistas para pasar de un grafo sin esquema a un esquema con tipo. También puedes optar por usar un grafo sin esquema para algunos casos de uso y un grafo de esquema con tipo para otros casos de uso. Para obtener más información, consulta Elige un diseño de esquema basado en consultas de grafos.

En el siguiente ejemplo, se muestra el flujo de trabajo para realizar la transición de un grafo sin esquema a uno formalizado en cuatro pasos:

  1. Define las tablas canónicas GraphNode y GraphEdge para el modelo sin esquema.

  2. Crear un grafo inicial y flexible en esas tablas sin esquema

  3. Define vistas con tipo (Person, Company, WorksFor) que extraigan y formalicen los datos de las tablas sin esquema.

  4. Crea el grafo final con tipado estático que use estas vistas como sus tablas de nodos y aristas.

-- 1. Create the canonical tables for a schemaless model.
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;

-- 2. Define a schemaless graph.
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)
  );

-- 3. Define typed views that extract and formalize the data.
--    Convert JSON fields to primitive types (for example, INT64, STRING) to
--    ensure type safety.
CREATE VIEW Person SQL SECURITY INVOKER AS
  SELECT n.id, STRING(n.properties.name) AS name, INT64(n.properties.age) AS age
  FROM GraphNode n WHERE n.label = "person";

CREATE VIEW Company SQL SECURITY INVOKER AS
  SELECT n.id, STRING(n.properties.name) AS company_name, BOOL(n.properties.is_public) AS is_public
  FROM GraphNode n WHERE n.label = "company";

CREATE VIEW WorksFor SQL SECURITY INVOKER AS
  SELECT e.id AS person_id, e.dest_id AS company_id, e.edge_id AS edge_id, STRING(e.properties.since) AS since
  FROM GraphEdge e
  WHERE e.label = "worksfor";

-- 4. Create the final, formalized graph from the typed views.
CREATE PROPERTY GRAPH typed_formalized_graph
  NODE TABLES (
    Person KEY(id)
      PROPERTIES (name, age),
    Company KEY(id)
      PROPERTIES (company_name, is_public)
  )
  EDGE TABLES(
    WorksFor KEY(person_id, company_id, edge_id)
      SOURCE KEY (person_id) REFERENCES Person(id)
      DESTINATION KEY (company_id) REFERENCES Company(id)
      PROPERTIES (since)
  );

¿Qué sigue?