Trabalhar com medidas

Este documento mostra como definir e consultar medidas nos seus gráficos. Você pode usar medidas para garantir que as agregações sejam realizadas corretamente em todas as junções.

Visão geral

Uma métrica é uma propriedade agregada definida na cláusula PROPERTIES de uma tabela de nós ou arestas. As métricas são definidas usando a palavra-chave MEASURE e uma das seguintes funções de agregação compatíveis:

  • SUM
  • AVG
  • COUNT
  • COUNT(DISTINCT)
  • MIN
  • MAX

As medidas definem a agregação em relação ao KEY da tabela de nós ou de arestas em que são definidas. Isso significa que, ao consultar uma métrica, a agregação é realizada corretamente, mesmo que a tabela subjacente seja unida de uma forma que cause a duplicação de linhas.

Não é possível referenciar uma propriedade definida por uma métrica em uma consulta GQL. Em vez disso, acesse as métricas chamando a TVF GRAPH_EXPAND para criar uma representação de tabela simplificada do seu gráfico. Essa função não aceita todos os tipos de gráficos. Para mais informações sobre quais grafos formam uma entrada válida, consulte as limitações de entrada.

É possível chamar a função AGG na saída da TVF GRAPH_EXPAND para agregar as propriedades de acordo com a função de agregação definida na métrica.

Definir medições

Você define medidas na cláusula PROPERTIES de uma definição de tabela de nós ou arestas usando a palavra-chave MEASURE() em torno de uma função de agregação compatível.

O exemplo a seguir cria um conjunto de dados chamado university e tabelas para faculdades, departamentos e cursos:

CREATE SCHEMA IF NOT EXISTS university;

CREATE OR REPLACE TABLE university.College (
  college_id INT64 PRIMARY KEY NOT ENFORCED,
  college_name STRING
);

CREATE OR REPLACE TABLE university.Department (
  dept_id INT64 PRIMARY KEY NOT ENFORCED,
  dept_name STRING,
  college_id INT64,
  budget FLOAT64,
  FOREIGN KEY (college_id) REFERENCES university.College(college_id) NOT ENFORCED
);

CREATE OR REPLACE TABLE university.Course (
  course_id INT64 PRIMARY KEY NOT ENFORCED,
  course_name STRING,
  dept_id INT64,
  credits INT64,
  FOREIGN KEY (dept_id) REFERENCES university.Department(dept_id) NOT ENFORCED
);

INSERT INTO university.College (college_id, college_name)
VALUES (101, 'College of Engineering'),
      (102, 'College of Arts');

INSERT INTO university.Department (dept_id, dept_name, college_id, budget)
VALUES (1001, 'Computer Science', 101, 500000),
      (1002, 'Mechanical Engineering', 101, 400000),
      (1003, 'Fine Arts', 102, 200000),
      (1004, 'Research', 101, 50000);

INSERT INTO university.Course (course_id, course_name, dept_id, credits)
VALUES (1, 'Intro to CS', 1001, 3),
       (2, 'Data Structures', 1001, 4),
       (3, 'Thermodynamics', 1002, 3),
       (4, 'Oil Painting', 1003, 2);

A instrução a seguir cria um gráfico chamado SchoolGraph que define medidas em algumas das propriedades nos nós Department e Course. É necessário fornecer um alias para as propriedades definidas por uma métrica.

CREATE OR REPLACE PROPERTY GRAPH university.SchoolGraph
  NODE TABLES (
    university.College
      KEY(college_id)
      PROPERTIES(college_id, college_name),
    university.Department
      KEY(dept_id)
      PROPERTIES(dept_id, dept_name, college_id,
        budget OPTIONS(description="Department budget in USD"),
        MEASURE(SUM(budget)) AS total_budget),
    university.Course
      KEY(course_id)
      PROPERTIES(
        course_id,
        course_name,
        credits,
        dept_id,
        MEASURE(AVG(credits)) AS avg_credits,
        MEASURE(SUM(credits)) AS total_credits,
        MEASURE(COUNT(course_id)) AS course_count)
  )
  EDGE TABLES (
    university.Department AS CollegeDept
      SOURCE KEY (college_id) REFERENCES College (college_id)
      DESTINATION KEY (dept_id) REFERENCES Department (dept_id),
    university.Course AS DeptCourse
      SOURCE KEY (dept_id) REFERENCES Department (dept_id)
      DESTINATION KEY (course_id) REFERENCES Course (course_id)
  );

Uma visualização do gráfico SchoolGraph.

A medida total_budget é definida como MEASURE(SUM(budget)). A métrica bloqueia a agregação para KEY, que é dept_id.

Como entender a contagem excessiva

Quando você une tabelas, os dados são repetidos sempre que há uma relação de um para muitos. Por exemplo, se você fizer a junção das tabelas Course, Department e College, um departamento com vários cursos vai aparecer em várias linhas na saída:

SELECT
  college_name,
  dept_name,
  course_name,
  budget
FROM university.Course
LEFT JOIN university.Department
  ON Course.dept_id = Department.dept_id
LEFT JOIN university.College
  ON Department.college_id = College.college_id;

/*------------------------+------------------------+-----------------+----------+
 | college_name           | dept_name              | course_name     | budget   |
 +------------------------+------------------------+-----------------+----------+
 | College of Engineering | Computer Science       | Intro to CS     | 500000.0 |
 | College of Engineering | Computer Science       | Data Structures | 500000.0 |
 | College of Engineering | Mechanical Engineering | Thermodynamics  | 400000.0 |
 | College of Arts        | Fine Arts              | Oil Painting    | 200000.0 |
 +------------------------+------------------------+-----------------+----------*/

Se você tentar calcular o orçamento total por faculdade usando SUM(budget), o orçamento do departamento de Ciência da Computação será contado duas vezes. Para evitar esse problema, consulte diretamente a tabela Department. No entanto, essa abordagem não funciona se você quiser calcular várias agregações de diferentes tabelas que contribuem para os dados combinados. A seção a seguir mostra como as métricas resolvem esse problema.

Consultar um gráfico com medidas

É possível consultar um gráfico com medidas usando GQL, mas a consulta não pode referenciar diretamente nenhuma propriedade definida por uma medida. Por exemplo, a consulta a seguir faz referência a nós que têm propriedades de métrica definidas, mas não usa nem retorna nenhum dos campos de métrica:

GRAPH university.SchoolGraph
MATCH (c:College)-[]-(d:Department)-[]->(course:Course)
RETURN c.college_name, d.dept_name, course.course_name;

/*------------------------+------------------------+-----------------+
 | college_name           | dept_name              | course_name     |
 +------------------------+------------------------+-----------------+
 | College of Engineering | Computer Science       | Intro to CS     |
 | College of Engineering | Computer Science       | Data Structures |
 | College of Engineering | Mechanical Engineering | Thermodynamics  |
 | College of Arts        | Fine Arts              | Oil Painting    |
 +------------------------+------------------------+-----------------*/

Trabalhar com medidas

Para trabalhar com medidas, use a função com valor de tabela (TVF) GRAPH_EXPAND para consultar seu gráfico como uma única tabela simplificada. As colunas na tabela de saída são derivadas das propriedades definidas no gráfico para cada tabela de nós e arestas. Para evitar conflitos de nomenclatura, as colunas são nomeadas concatenando o rótulo da tabela de nós ou arestas e o nome da propriedade. Por exemplo, Course_course_name ou Department_total_budget. A consulta a seguir mostra alguns exemplos de saída da TVF GRAPH_EXPAND:

SELECT
  College_college_name,
  Department_dept_name,
  Department_budget,
  Course_course_name
FROM
  GRAPH_EXPAND("university.SchoolGraph");

/*------------------------+------------------------+-------------------+--------------------+
 | College_college_name   | Department_dept_name   | Department_budget | Course_course_name |
 +------------------------+------------------------+-------------------+--------------------+
 | College of Engineering | Computer Science       | 500000.0          | Intro to CS        |
 | College of Engineering | Computer Science       | 500000.0          | Data Structures    |
 | College of Engineering | Mechanical Engineering | 400000.0          | Thermodynamics     |
 | College of Arts        | Fine Arts              | 200000.0          | Oil Painting       |
 +------------------------+------------------------+-------------------+--------------------+

A função GRAPH_EXPAND produz a tabela simplificada aplicando uma série de operações LEFT JOIN às tabelas de nós e arestas. Um gráfico de entrada válido precisa ter exatamente uma tabela de nós raiz, que é uma tabela cujo valor KEY não aparece em nenhuma outra tabela. Os dados que não podem ser acessados da tabela do nó raiz por uma série de junções não aparecem na saída. No exemplo anterior, a tabela Course é a tabela de nós raiz. O departamento Research foi omitido da saída porque não tem cursos.

Não é possível selecionar diretamente uma coluna para uma propriedade definida por uma medida. Em vez disso, é necessário envolvê-las na função AGG(). Essa função garante que a agregação definida nas medidas seja realizada exatamente uma vez por chave.

A consulta a seguir calcula simultaneamente o orçamento total e o número total de cursos de cada faculdade:

SELECT
  College_college_name,
  AGG(Department_total_budget) AS college_budget,
  AGG(Course_course_count) AS total_courses
FROM
  GRAPH_EXPAND("university.SchoolGraph")
GROUP BY
  College_college_name;

/*------------------------+----------------+---------------+
 | College_college_name   | college_budget | total_courses |
 +------------------------+----------------+---------------+
 | College of Engineering | 900000.0       | 3             |
 | College of Arts        | 200000.0       | 1             |
 +------------------------+----------------+---------------*/

Práticas recomendadas

Ao criar um gráfico com medidas, siga estas práticas recomendadas:

Definir nós e arestas em uma única tabela

Recomendamos usar a mesma tabela para uma definição de tabela de nós e as definições de tabela de arestas de conexão. A reutilização da tabela garante uma relação exata de um para um (1:1) entre a tabela de nós e a tabela de arestas, o que evita a distribuição de dados e garante que a função GRAPH_EXPAND não ignore a aresta como ambígua.

Por exemplo, se você tiver uma tabela de entrada Enrollment que contém chaves estrangeiras apontando para as tabelas Student e Course, defina o nó Enrollment e as arestas de saída usando a tabela de entrada Enrollment.

Modelar relações de muitos para muitos como tabelas de nós

Uma relação de muitos para muitos (M:N) ocorre quando uma linha na tabela A pode corresponder a várias linhas na tabela B, e uma linha na tabela B pode corresponder a várias linhas na tabela A. Por exemplo, um estudante pode se inscrever em vários cursos, e um curso pode ter muitos estudantes. É possível separar uma relação de muitos para muitos em duas relações de um para muitos usando uma tabela de junção intermediária. Por exemplo, é possível definir uma tabela Enrollment que contém chaves externas apontando para as tabelas Student e Course. Se você modelar uma tabela de junção intermediária como uma tabela de arestas que conecta duas tabelas de nós de dimensão na sua instrução CREATE PROPERTY GRAPH, a função GRAPH_EXPAND vai ignorar essas arestas porque a relação delas é ambígua: não é claramente de muitos para um ou de um para um.

CREATE PROPERTY GRAPH university.SchoolGraph
  NODE TABLES (
    university.Student,
    university.Course
  )
  EDGE TABLES (
    -- Modeling the junction table as a standalone edge causes ambiguity
    university.Enrollment AS StudentEnrollment
      SOURCE KEY (student_id) REFERENCES Student (student_id)
      DESTINATION KEY (course_id) REFERENCES Course (course_id)
  );

A função GRAPH_EXPAND ignora essas arestas porque elas são ambíguas (não são claramente de muitos para um ou de um para um entre as próprias tabelas de nós). Se todas as arestas em um gráfico forem ignoradas, as chamadas para GRAPH_EXPAND ou BQ.SHOW_GRAPH_EXPAND_SCHEMA vão falhar com um erro.

Para modelar corretamente uma relação M:N para medidas, promova a tabela de junção intermediária para uma tabela de nós. Nessa estrutura, a tabela de nós de junção serve como a única tabela de nós raiz (uma tabela de nós com um grau de entrada zero). Em seguida, defina duas tabelas de arestas de muitos para um (N:1) que compartilham a tabela de junção para conectar N:1 a cada uma das tabelas de nós de dimensão:

CREATE PROPERTY GRAPH university.SchoolGraph
  NODE TABLES (
    university.Student KEY (student_id),
    university.Course KEY (course_id),
    -- Promote the junction table to a node table (acting as the single root node)
    university.Enrollment KEY (enrollment_id)
  )
  EDGE TABLES (
    -- Share (reuse) the Enrollment table to define N:1 edges to each dimension
    university.Enrollment AS EnrollmentToStudent
      SOURCE KEY (enrollment_id) REFERENCES Enrollment (enrollment_id)
      DESTINATION KEY (student_id) REFERENCES Student (student_id),
    university.Enrollment AS EnrollmentToCourse
      SOURCE KEY (enrollment_id) REFERENCES Enrollment (enrollment_id)
      DESTINATION KEY (course_id) REFERENCES Course (course_id)
  );

Nessa estrutura, Enrollment serve como a única tabela de nó raiz (grau de entrada zero) que conecta N:1 às tabelas de dimensões Student e Course (grau de entrada um). A função GRAPH_EXPAND pode simplificar esse gráfico percorrendo para fora de Enrollment até Student e Course. Se você definir medidas em qualquer uma dessas tabelas, poderá chamar a função AGG nas colunas de medida para calcular corretamente as agregações sem contagem excessiva.

Ver esquema GRAPH_EXPAND

Para conferir o esquema da tabela retornada pela função GRAPH_EXPAND sem chamar a função, use o procedimento do sistema BQ.SHOW_GRAPH_EXPAND_SCHEMA:

DECLARE schema STRING DEFAULT '';
CALL BQ.SHOW_GRAPH_EXPAND_SCHEMA('university.SchoolGraph', schema);
SELECT schema;

Esse procedimento preenche a variável schema com o nome, o tipo e o modo de cada coluna. Ele também indica se a propriedade referenciada pela coluna é uma métrica e lista qualquer descrição ou sinônimo definido nela.

A seguir