2 pontos por GN⁺ 3 시간 전 | Ainda não há comentários. | Compartilhar no WhatsApp
  • O grafo de propriedades do Postgres 19 é um recurso de SQL/PGQ que declara tabelas existentes como vértices e arestas e pesquisa padrões fixos de relacionamento com MATCH, sem copiar dados nem criar um mecanismo de execução de grafos separado
  • Os padrões de grafo são compilados em joins relacionais, reutilizando o otimizador, os índices e as estatísticas existentes, então até travessias lentas podem ser analisadas e otimizadas da mesma forma que joins comuns
  • Tabelas de dimensão correspondem naturalmente a vértices, e tabelas de junção puras a arestas, mas tabelas fato com várias chaves estrangeiras e propriedades próprias são mais adequadas para modelagem como vértices-hub que representam eventos
  • É possível declarar a mesma tabela ao mesmo tempo como vértice e como várias arestas usando aliases diferentes, então não é necessário criar nem armazenar tabelas de aresta separadas usando as chaves estrangeiras já existentes em results
  • Como o Postgres 19 não oferece suporte a caminhos de comprimento variável, ele não é adequado para menor caminho, alcançabilidade em N saltos ou PageRank; funciona melhor para buscas de comprimento fixo quando a estrutura relacional já é conhecida

Como ler um esquema relacional como grafo

  • Em um esquema relacional normalizado, tabelas de dimensão como drivers, constructors e circuits armazenam entidades, enquanto tabelas fato registram eventos como resultados de corrida ou sessões classificatórias
  • Cada linha é um vértice em potencial, e chaves estrangeiras são arestas em potencial que apontam para outras linhas
    • Um diagrama ER corresponde a um grafo no nível do esquema
    • As linhas reais e seus relacionamentos por chave estrangeira correspondem a um grafo no nível da instância
  • Um pedido como “encontrar o constructor correspondente a cada resultado” é uma travessia de grafo que, em SQL relacional, seria escrita como results JOIN constructors ON ...
  • O SQL/PGQ não adiciona um novo grafo em si; em vez de listar joins manualmente, ele permite escrever a pergunta na forma de grafo
  • A abordagem do RelBench leva tabelas para pandas e monta um grafo PyTorch em memória, enquanto o grafo de propriedades do Postgres usa diretamente os relacionamentos já existentes entre tabelas dentro do banco de dados
  • Os exemplos do PyG também usam principalmente arquivos planos ou grafos em memória
  • O código do experimento com o conjunto de dados de Fórmula 1 pode ser visto no repositório no GitHub

Composição de um grafo de propriedades

  • CREATE PROPERTY GRAPH é um objeto declarativo nomeado criado sobre tabelas existentes
    • VERTEX TABLES especifica quais tabelas terão suas linhas lidas como vértices
    • EDGE TABLES especifica quais tabelas terão suas linhas lidas como relacionamentos de ligação
  • Para cada tabela de vértice, são definidos os seguintes elementos
    • KEY: identificador do vértice, normalmente a chave primária
    • LABEL: nome do tipo de vértice usado em MATCH
    • PROPERTIES: lista de colunas acessíveis nas consultas de grafo
  • Para cada tabela de aresta, definem-se SOURCE e DESTINATION, e ambos referenciam as chaves dos vértices
  • CREATE PROPERTY GRAPH não move nem copia os dados
    • As linhas continuam nas tabelas originais
    • A declaração apenas define como ler a estrutura existente de chaves estrangeiras como grafo
  • Os elementos estruturais de um grafo de propriedades são de apenas dois tipos: vértices e arestas
    • Labels e propriedades são atributos pertencentes a um vértice ou a uma aresta
    • No psql, o Element Kind de cada elemento é vertex ou edge

Consultando padrões fixos com MATCH

  • Consultas de grafo são escritas colocando um padrão MATCH dentro de GRAPH_TABLE(...)
  • O padrão abaixo representa o relacionamento que vai de driver para race passando por result
MATCH (d IS driver)<-[IS of_driver]-(res IS result)-[IS in_race]->(ra IS race)  
  • A cláusula COLUMNS define quais colunas da consulta de grafo serão retornadas para fora
  • No SELECT externo, o resultado de GRAPH_TABLE(...) pode ser consultado como uma tabela comum
  • Como o padrão inclui direção e labels, o relacionamento pode ser lido como uma frase, o que facilita entender a estrutura em comparação com o mesmo caso usando múltiplos joins

Estrutura convertida em joins relacionais na execução

  • MATCH não é um mecanismo de execução de grafos separado acoplado ao Postgres; é uma sintaxe compilada em joins relacionais
  • Ao executar EXPLAIN no padrão driver–result–race, aparecem quatro hash joins sobre as tabelas de base
  • Assim como nos joins escritos manualmente, ele usa
    • o otimizador já existente do Postgres
    • os índices já existentes
    • as estatísticas já existentes
  • Se uma travessia de grafo estiver lenta, a causa e a forma de otimizar serão as mesmas de um join comum lento
  • Também é possível inspecionar grafos no psql com comandos parecidos com os de tabelas
    • \dG: mostra a lista de grafos de propriedades
    • \d f1: mostra cada vértice e aresta, a tabela de base, o tipo de elemento e os vértices de origem e destino das arestas
    • \d+ f1: reconstrói toda a instrução CREATE PROPERTY GRAPH, incluindo a chave da aresta inferida a partir da chave primária da tabela de aresta

Diferença entre chaves e propriedades

  • Uma coluna definida como KEY de um vértice não se torna automaticamente uma propriedade
  • Se driver_id for definido apenas como chave do vértice, um filtro como d.driver_id = 1 falhará porque essa coluna não é uma propriedade consultável
  • A chave identifica o vértice, mas não o expõe à consulta
  • Para filtrar ou retornar uma coluna de ID, é preciso adicioná-la explicitamente à lista de PROPERTIES

Limitação de caminhos de comprimento variável no Postgres 19

  • O Postgres 19 não oferece suporte a quantificadores de padrão de elemento como seguir uma aresta de 1 a 3 vezes
ERROR: element pattern quantifier is not supported  
  • Para percorrer dois saltos, é preciso declarar explicitamente dois padrões de aresta em MATCH
  • Caminhos de comprimento arbitrário não podem ser expressos com a sintaxe de grafo de propriedades
  • Buscas com profundidade aberta precisam ser tratadas com CTE recursiva sobre as tabelas de base, o que fica fora da sintaxe de grafo de propriedades

Mapeando tabelas existentes para vértices e arestas

  • Tabelas de dimensão como vértices

    • Tabelas de entidades com chave primária estável e atributos, como drivers, constructors e circuits, correspondem diretamente a vértices
    • Basta usar a chave primária como chave do vértice e expor como propriedades as colunas necessárias
  • Tabelas de junção puras como arestas

    • Tabelas-ponte muitos-para-muitos como student_courses(student_id, course_id) têm como função original ligar duas entidades, então correspondem naturalmente a arestas
    • Uma chave estrangeira é declarada como vértice de origem e a outra como vértice de destino
    • Como cada linha da tabela de junção já é um relacionamento, não é necessária nenhuma transformação adicional de dados para o grafo
    • Nessa estrutura, students e courses são vértices, e student_courses se torna a aresta enrolled_in
  • Tabelas fato como vértices de evento

    • Linhas de results apontam para três entidades — driver, race e constructor — e ainda têm dados próprios como grid, position, points e status
    • Como arestas em SQL/PGQ são relações binárias com uma origem e um destino, não dá para transformar uma linha inteira com três chaves estrangeiras em uma única aresta
    • Se a própria linha fato for objeto de análise, é mais adequado declarar essa tabela como vértice
    • O vértice results armazena o evento e suas propriedades
    • Arestas estreitas como results_driver, results_race e results_constructor conectam esse vértice às entidades externas
    • Esse modelo cria uma estrutura de hub como driver <- result -> race, na qual é possível parar no vértice result para filtrar ou consultar propriedades
    • Se o objeto de interesse for o relacionamento em si, modele como aresta; se for uma linha específica ou evento com propriedades próprias, modele como vértice
    • Tabelas de junção representam relacionamentos, enquanto tabelas fato representam eventos

Usando a mesma tabela ao mesmo tempo como vértice e aresta

  • A distinção “vértice ou aresta” não se aplica à tabela de base, e sim a cada declaração de elemento no grafo
  • A mesma tabela pode ser declarada tanto em VERTEX TABLES quanto em EDGE TABLES usando aliases diferentes
  • É possível usar results como vértice result e reutilizar a mesma tabela com os seguintes aliases de aresta
    • results AS res_driver: conecta result a driver
    • results AS res_race: conecta result a race
    • results AS res_constr: conecta result a constructor
  • Cada alias de aresta usa as colunas de chave primária e chave estrangeira já existentes em results
  • Não é necessário criar tabelas separadas como results_driver, results_race e results_constructor, e os aliases não armazenam dados
  • Uma tabela fato com três chaves estrangeiras não deve ser declarada como uma única aresta com múltiplos destinos, mas sim como três aliases de aresta, cada um com uma origem e um destino
  • Essa abordagem usa três aliases declarativos sobre uma única tabela de base, em vez de três tabelas físicas ou views

Conflitos de nome e tipo de propriedades

  • Se PROPERTIES for omitido, o SQL/PGQ expõe todas as colunas da tabela como propriedades
  • Se results e qualifying tiverem ambas uma coluna number, mas com tipos double precision e bigint, ocorrerá o seguinte erro
ERROR: property "number" data type mismatch: double precision vs. bigint  
  • Em todo o grafo, propriedades com o mesmo nome precisam ter um único tipo, então colunas homônimas com tipos diferentes entram em conflito
  • Ao especificar apenas as colunas necessárias em PROPERTIES, é possível excluir do grafo as colunas conflitantes
  • Tanto o problema de chaves não virarem propriedades automaticamente quanto os conflitos de tipo podem ser resolvidos com uma lista explícita de propriedades permitidas

Consultas adequadas e inadequadas

  • Padrões relacionais fixos

    • Grafos de propriedades são adequados para perguntas do tipo “encontre os alvos conectados a X por este padrão específico”
    • No grafo de Fórmula 1, é possível escrever consultas como
      1. descobrir por quais constructors um driver correu
      2. encontrar competidores seguindo um padrão que vai de driver para result e race, depois para outro result e outro driver
      3. filtrar ao mesmo tempo estrutura e condições de propriedade, como resultados em que a posição de largada foi pior que 10 e o driver era italiano
    • Quando o formato do relacionamento é conhecido de antemão e a estrutura é fixa e finita, a sintaxe de busca, filtro e agregação fica mais legível do que em joins relacionais
    • Até consultas que exigiriam vários self-joins podem ser expressas de forma mais clara em um único padrão MATCH
  • Problemas em que o caminho em si é desconhecido

    • Os casos abaixo não se encaixam bem nos grafos de propriedades do Postgres 19
      • o menor caminho entre dois drivers
      • todos os alvos alcançáveis em até N saltos
      • busca de conectividade cuja profundidade não é conhecida previamente
    • Essas consultas exigem travessias de comprimento variável, então é preciso usar CTE recursiva sobre as tabelas de base
    • Algoritmos de grafo como PageRank, detecção de comunidades e cálculo de centralidade também são um problema diferente de correspondência de padrões e, por isso, não fazem parte do escopo desse recurso
    • O grafo de propriedades encontra entidades conectadas quando a estrutura do caminho é conhecida, mas não descobre caminhos quando não se sabe como X e Y estão conectados, nem calcula a importância estrutural do grafo

O que avaliar antes de adotar

  • SQL/PGQ é um overlay declarativo sobre a estrutura existente de chaves estrangeiras e, a menos que você escolha o contrário, não armazena dados adicionais
  • Há dois benefícios principais
    • escrever travessias de forma fixa com mais legibilidade do que joins relacionais
    • fornecer um objeto nomeado que documenta o esquema como grafo
  • A maior limitação é a ausência de caminhos de comprimento variável, o que obriga a tratar buscas profundas ou abertas com SQL recursivo
  • As tabelas em si não são inerentemente vértices nem arestas; em cada grafo, você decide se aquelas linhas serão lidas como vértices, arestas ou ambos
  • Se a intenção for consultar padrões fixos de grafo sobre um esquema existente no Postgres 19, vale avaliar CREATE PROPERTY GRAPH e MATCH antes de mover os dados para um banco de grafos separado
  • Se for preciso explorar profundidades desconhecidas, será necessária uma CTE recursiva, mas em qualquer abordagem os dados não precisam sair do Postgres
  • Antes de adotar de fato, é preciso testar diretamente se o desempenho atende ao necessário

Ainda não há comentários.

Ainda não há comentários.