DevGuideTK
← Trilha Backend

Bancos de grafos

Quando os relacionamentos são o dado principal, como em redes sociais e detecção de fraude.

O problema: perguntas sobre caminhos

Algumas perguntas não são sobre os dados, e sim sobre as ligações entre eles:

  • Quem são os amigos dos meus amigos que eu ainda não sigo?
  • Esta conta nova usa o mesmo aparelho, o mesmo cartão ou o mesmo endereço de alguma conta já bloqueada por fraude, mesmo que indiretamente?
  • Qual é a menor cadeia de indicações entre estas duas pessoas?
  • Se este servidor cair, quais serviços param, direta ou indiretamente?

No modelo relacional, cada passo do caminho é uma junção. "Amigos de amigos" são duas junções da tabela consigo mesma; "a até cinco passos" são cinco, ou uma consulta recursiva. Um banco de grafos guarda as ligações como parte do dado: cada nó sabe quais são os vizinhos, e a consulta descreve o padrão do caminho.

Nós, arestas e Cypher

O grafo tem nós, com rótulos e propriedades, e arestas (relacionamentos), com tipo, direção e propriedades:

(:Pessoa {nome: "Ana"}) -[:SEGUE {desde: 2024}]-> (:Pessoa {nome: "Bia"})

No Neo4j, a linguagem de consulta é o Cypher, que desenha o padrão com parênteses para os nós e setas para as arestas. Os amigos de amigos que eu ainda não sigo:

MATCH (eu:Pessoa {nome: "Ana"})-[:SEGUE]->(:Pessoa)-[:SEGUE]->(sugestao:Pessoa)
WHERE sugestao <> eu AND NOT (eu)-[:SEGUE]->(sugestao)
RETURN sugestao.nome, count(*) AS em_comum
ORDER BY em_comum DESC

A ideia central do Neo4j é a adjacência sem índice: cada nó guarda referências diretas às suas arestas. Percorrer um passo custa o mesmo com mil ou com um bilhão de nós, porque não depende de buscar num índice a cada salto. Em 2024, a ISO publicou o GQL, um padrão de linguagem de consulta de grafos inspirado no Cypher.

Grafo ou recursão no PostgreSQL, medido

O senso comum diz que o banco relacional não aguenta consultas de grafos. Medimos o mesmo grafo, com 100 mil pessoas e 1 milhão de relações "segue", no Neo4j 5.26 e no PostgreSQL 18, com uma consulta recursiva e índices nas duas colunas da tabela de relações. Cada banco tinha 4 núcleos e 4 GB de memória.

Pessoas alcançáveis a partir de uma pessoa, a até N passos (tempos da segunda execução, com a consulta já planejada):

PassosPessoas alcançadasPostgreSQLNeo4j
49.78711 ms6 ms
544.23061 ms33 ms
649.999248 ms160 ms
749.999513 ms171 ms

Menor caminho entre duas pessoas (o resultado foi 5 passos nos dois bancos):

Tempo
Neo4j, shortestPath1 ms
PostgreSQL, busca recursiva até 7 passos488 ms

Até 4 ou 5 passos, a recursão do PostgreSQL ficou na mesma ordem de grandeza do Neo4j. Com a profundidade, a distância cresceu: três vezes a 7 passos. E no menor caminho, a diferença foi de quase 500 vezes. O Neo4j busca a partir das duas pontas e para assim que os caminhos se encontram; a consulta recursiva expande tudo até a profundidade máxima e só depois procura o destino.

Na primeira execução, o Neo4j levou entre 65 e 150 ms só para planejar cada consulta. Em produção, consultas parametrizadas reaproveitam o plano.

A explosão de caminhos

Num grafo em que cada pessoa segue 10 outras, há 10 caminhos de um passo, 100 de dois, um milhão de seis. Contar pessoas distintas é diferente de contar caminhos:

A até 6 passosResultadoTempo
PostgreSQL, UNION (remove repetidos a cada nível)49.999 pessoas248 ms
PostgreSQL, UNION ALL (todos os caminhos)1.111.111 caminhos815 ms
Neo4j, com count(DISTINCT x)49.999 pessoas160 ms
Neo4j, sem DISTINCT (todos os caminhos)1.111.100 caminhos602 ms

Mais de um milhão de caminhos para chegar a 50 mil pessoas. Num grafo real, com pessoas que seguem centenas de outras, e sem limite de profundidade, essa conta não termina. Por isso, toda consulta de caminho variável precisa de um limite de profundidade (*1..4 no Cypher, WHERE d < 4 na recursão), e de remover os repetidos sempre que o que importa são os nós, e não os caminhos.

Onde os grafos brilham

UsoA pergunta de grafo
Detecção de fraudeContas que compartilham aparelhos, cartões ou endereços com contas bloqueadas, a vários passos
RecomendaçãoQuem compra o que as pessoas parecidas comigo compram
Redes sociaisAmigos de amigos, influência, comunidades
DependênciasO que para se este componente cair; ciclos de dependência
Permissões por relaçãoO usuário é membro do time dono da pasta onde está o documento (o ReBAC do tópico de autorização)
Grafos de conhecimentoEntidades e relações para busca e para alimentar modelos de linguagem

E os algoritmos: menor caminho, centralidade (quem é mais conectado), detecção de comunidades, similaridade. O Neo4j oferece esses algoritmos numa biblioteca própria (Graph Data Science).

Por que isso importa quando a IA escreve o código

Um assistente que modela relacionamentos costuma errar para um dos dois lados:

  • Escolher um banco de grafos para dados que são tabelas com algumas chaves estrangeiras.
  • Ou o contrário: encadear junções em código para caminhos de profundidade variável.
  • Consultas de caminho variável sem limite de profundidade (-[:SEGUE*]->).
  • Contar caminhos quando a pergunta é sobre nós, sem DISTINCT.
  • Consultas Cypher montadas por concatenação de texto, em vez de parâmetros, abrindo espaço para injeção e impedindo o reaproveitamento do plano.
  • Nós sem restrição de unicidade na propriedade usada para encontrá-los, gerando duplicatas com CREATE em vez de MERGE.

O próximo nível compara quando usar um banco de grafos, como modelar e as alternativas.

Prompts para aprofundar

Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.

  • Bancos de grafos em cinco minutos

    Explique em no máximo cinco minutos de leitura o que é um banco de grafos, para alguém que só usou bancos relacionais.
    
    Cubra só o essencial: nós, arestas e propriedades, como uma consulta Cypher descreve um caminho, por que percorrer ligações é rápido, a explosão de caminhos e quando o PostgreSQL com consultas recursivas basta.
    
    Use os exemplos de amigos de amigos e de detecção de fraude. No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
  • Aprender Cypher pelos padrões

    Quero aprender a escrever consultas Cypher.
    
    Aja como um mentor e vá por etapas, esperando a minha resposta em cada uma:
    1. A sintaxe de nós, arestas, rótulos e propriedades num MATCH.
    2. Filtros com WHERE, inclusive padrões negativos como NOT (a)-[:SEGUE]->(b).
    3. Agregações com count, collect e ORDER BY.
    4. Caminhos de comprimento variável, com limite de profundidade e DISTINCT.
    5. MERGE, CREATE e restrições de unicidade.
    
    Em cada etapa, me dê um problema sobre um grafo de pessoas e compras, peça que eu escreva a consulta e corrija a minha resposta.
  • Grafo ou recursão no relacional

    Quero entender quando um banco de grafos é mesmo melhor que o PostgreSQL com consultas recursivas.
    
    Explique:
    1. Como uma consulta WITH RECURSIVE percorre um grafo guardado numa tabela de arestas.
    2. A adjacência sem índice dos bancos de grafos e o que ela muda em cada passo.
    3. Por que a profundidade e o menor caminho aumentam a diferença.
    4. A explosão de caminhos, com UNION e UNION ALL na recursão, e DISTINCT no Cypher.
    5. Extensões como Apache AGE.
    
    Depois me descreva quatro problemas e pergunte em qual eu usaria grafo e em qual a recursão.
  • Detecção de fraude com grafos

    Quero entender como grafos ajudam a detectar fraude.
    
    Explique, com um exemplo de modelo:
    1. Por que aparelhos, cartões, endereços e telefones viram nós, e não propriedades.
    2. Como achar contas ligadas a uma conta bloqueada, a vários passos.
    3. Como identificar anéis de contas que compartilham dados.
    4. O problema dos supernós (um endereço de centro de distribuição usado por milhares).
    5. Algoritmos de comunidades e centralidade aplicados à fraude.
    
    Depois me dê um cenário e peça que eu escreva a consulta que encontraria a fraude.