# Grafos no SurrealDB: 4 aplicações para produtos brasileiros
> Veja como usar relações de grafo em comunidades, educação, marketplaces e atribuição de marketing sem transformar cada conexão em uma pilha de joins.
**Autores:** Mauro Mequelussi
**Publicado:** 2026-07-16T12:00:00.000Z
**Atualizado:** 2026-07-20T15:00:47.697650465Z
**Tags:** comunidade, vibe-coding, surrealql, grafos, surrealdb, marketplace
---
Tabelas guardam coisas. Produtos reais também precisam guardar conexões: quem indicou quem, qual aluno concluiu qual aula, qual profissional atende qual região, quais pessoas pertencem a uma comunidade e como uma compra chegou até uma campanha.

Quando essas conexões respondem perguntas importantes, um **grafo** deixa de ser uma escolha exótica e vira uma forma direta de representar o negócio. SurrealDB permite combinar registros, documentos e relações no mesmo banco, usando arestas que também carregam dados.

## O que é um grafo no banco de dados?

Um grafo é formado por nós e arestas. Nós representam entidades — pessoa, curso, produto, cidade. Arestas representam relações — matriculou-se, indicou, comprou, atende — e podem guardar propriedades como data, papel, origem e status.

No SurrealDB, a relação é criada com `RELATE` e percorrida com setas:

::code-demo{language="surql" filename="database/grafo-minimo.surql"}
```surql
RELATE profile:ana->enrolled_in->course:surrealdb
  SET status = 'active',
      source = 'pix',
      enrolled_at = time::now();

SELECT ->enrolled_in->course.{ title, level }
FROM profile:ana;
```
::

O curso e o perfil continuam sendo registros normais. `enrolled_in` é uma relação com significado e histórico próprios.

## Quando usar relação de grafo ou record link?

Nem toda referência precisa virar aresta.

| Situação | Modelo preferível |
| --- | --- |
| pedido pertence a um cliente | campo `record<cliente>` |
| usuário segue outro usuário | relação `follows` |
| artigo possui um autor principal | record link |
| aluno se matricula e a matrícula tem status/data | relação `enrolled_in` |
| endereço atual de entrega | objeto ou record link |
| indicação precisa guardar campanha e recompensa | relação `referred` |

Use record link quando a conexão é simples e unilateral. Use relation table quando o vínculo tem propriedades, regras, unicidade, histórico ou precisa ser percorrido em várias direções.

::callout{type="info" title="A relação também é dado"}
Se você precisa responder “quando começou?”, “em qual papel?”, “por qual origem?” ou “com qual resultado?”, provavelmente a conexão merece um registro próprio.
::

## Cenário 1: comunidade e rede de indicações

Uma comunidade brasileira pode recomendar conexões, premiar indicações e mostrar conteúdo de pessoas seguidas. O modelo base inclui perfis e duas relações.

::code-demo{language="surql" filename="database/community.surql"}
```surql
DEFINE TABLE profile SCHEMAFULL;
DEFINE FIELD name ON profile TYPE string;
DEFINE FIELD city ON profile TYPE option<string>;
DEFINE FIELD joined_at ON profile TYPE datetime DEFAULT time::now();

DEFINE TABLE follows SCHEMAFULL
  TYPE RELATION IN profile OUT profile;
DEFINE FIELD since ON follows TYPE datetime DEFAULT time::now();
DEFINE INDEX follows_unique ON follows FIELDS in, out UNIQUE;

DEFINE TABLE referred SCHEMAFULL
  TYPE RELATION IN profile OUT profile;
DEFINE FIELD campaign ON referred TYPE option<string>;
DEFINE FIELD converted_at ON referred TYPE option<datetime>;
DEFINE FIELD reward_centavos ON referred TYPE int DEFAULT 0;
DEFINE INDEX referral_unique ON referred FIELDS in, out UNIQUE;
```
::

Uma sugestão “pessoas que você talvez conheça” pode percorrer dois saltos: pessoas seguidas por quem Ana segue, excluindo a própria Ana e quem ela já segue.

O [tutorial oficial de grafos sociais](https://surrealdb.com/blog/social-graphs-for-activity-tracking-apps) mostra como seguidores, comentários, clubes e rankings podem coexistir em um modelo desse tipo. A adaptação brasileira não está em traduzir nomes; está em escolher conexões que geram valor local, como indicação, região, turma, parceiro ou revendedor.

::read-more{slug="grafos-surrealdb-produtos-brasileiros" label="Continuar: grafos para educação, marketplace e marketing" placement="mid_content"}
## Cenário 2: educação e jornada do aluno

Em uma plataforma de cursos, uma tabela de matrículas resolve o básico. O grafo se torna mais interessante quando o produto precisa conectar pré-requisitos, trilhas, habilidades e progresso.

::code-demo{language="surql" filename="database/learning-graph.surql"}
```surql
DEFINE TABLE course SCHEMAFULL;
DEFINE FIELD title ON course TYPE string;
DEFINE FIELD level ON course TYPE string
  ASSERT $value IN ['beginner', 'intermediate', 'advanced'];

DEFINE TABLE skill SCHEMAFULL;
DEFINE FIELD name ON skill TYPE string;

DEFINE TABLE teaches SCHEMAFULL
  TYPE RELATION IN course OUT skill;
DEFINE FIELD weight ON teaches TYPE decimal
  ASSERT $value > 0dec AND $value <= 1dec;

DEFINE TABLE requires SCHEMAFULL
  TYPE RELATION IN course OUT course;
DEFINE FIELD mandatory ON requires TYPE bool DEFAULT true;

DEFINE TABLE enrolled_in SCHEMAFULL
  TYPE RELATION IN profile OUT course;
DEFINE FIELD progress ON enrolled_in TYPE decimal DEFAULT 0dec
  ASSERT $value >= 0dec AND $value <= 1dec;
DEFINE FIELD status ON enrolled_in TYPE string
  ASSERT $value IN ['active', 'completed', 'cancelled'];
```
::

Agora o produto pode perguntar: quais habilidades este curso ensina? Quais cursos exigem um curso anterior? Qual é o próximo conteúdo coerente com aquilo que o aluno concluiu?

Recomendação não precisa começar com IA. Uma travessia bem modelada costuma ser mais explicável e barata que enviar todo o catálogo para um modelo.

## Cenário 3: marketplace de serviços locais

Um marketplace conecta prestadores, serviços, cidades, avaliações e disponibilidade. A pergunta de busca não é apenas “qual profissional tem esta categoria?”, mas “quem atende minha região, oferece o serviço, está disponível e possui sinais de confiança?”.

Um modelo híbrido funciona bem:

- `provider`, `service` e `city` como nós;
- `offers` para preço inicial, duração e modalidade;
- `serves` para raio ou área atendida;
- `reviewed` para nota, comentário e pedido verificado;
- agenda como registros temporais, quando travessia de grafo não acrescentar valor.

::code-demo{language="surql" filename="database/marketplace.surql"}
```surql
DEFINE TABLE offers SCHEMAFULL
  TYPE RELATION IN provider OUT service;
DEFINE FIELD price_from_centavos ON offers TYPE int
  ASSERT $value >= 0;
DEFINE FIELD mode ON offers TYPE string
  ASSERT $value IN ['online', 'presencial', 'hibrido'];
DEFINE INDEX offer_unique ON offers FIELDS in, out UNIQUE;

DEFINE TABLE serves SCHEMAFULL
  TYPE RELATION IN provider OUT city;
DEFINE FIELD radius_km ON serves TYPE option<decimal>;

DEFINE TABLE reviewed SCHEMAFULL
  TYPE RELATION IN customer OUT provider;
DEFINE FIELD rating ON reviewed TYPE int
  ASSERT $value >= 1 AND $value <= 5;
DEFINE FIELD comment ON reviewed TYPE option<string>;
DEFINE FIELD verified_order ON reviewed TYPE record<order>;
```
::

Não guarde dinheiro em `float`. Centavos inteiros ou um tipo decimal cuidadosamente tratado são escolhas mais seguras. Também não deixe uma avaliação nascer apenas com IDs enviados pelo navegador: confirme que o pedido pertence ao cliente e foi concluído.

## Cenário 4: atribuição de marketing sem perder a jornada

Produtos brasileiros dependem de WhatsApp, indicação, anúncios e conteúdo orgânico. Uma conversão raramente tem um único ponto de contato.

Um grafo pode ligar visitante, sessão, campanha, página, lead e compra. A relação carrega horário, canal, identificador de clique e posição na jornada. Isso permite consultar caminhos sem esmagar tudo num campo `utm_source` do cadastro final.

O cuidado é proporcional ao valor desses dados. Identificadores de publicidade, telefone e histórico de navegação podem se tornar dados pessoais. Defina retenção, acesso e finalidade; não colete “porque talvez um dia seja útil”.

## Unicidade e idempotência pertencem às arestas

Seguir alguém duas vezes, dar dois likes iguais ou criar duas matrículas ativas pode corromper métricas e cobrar duas vezes uma automação. Use índice único nas relações em que apenas um vínculo pode existir.

Quando múltiplas relações são legítimas — várias compras do mesmo produto, por exemplo — use um ID idempotente derivado do evento ou uma chave única do provedor de pagamento. O modelo precisa distinguir repetição acidental de recorrência real.

## Grafo não dispensa índice nem limite

Uma travessia elegante ainda pode ser cara. Antes de colocar uma consulta em produção:

- limite profundidade e quantidade de resultados;
- filtre cedo por status, tenant ou período;
- indexe campos usados fora da travessia;
- teste com volume parecido com o esperado;
- observe tempo, memória e cardinalidade;
- evite retornar subgrafos inteiros para o navegador.

Consultas “amigos de amigos de amigos” crescem rapidamente. A pergunta de produto deve determinar até onde percorrer.

## Como pedir ajuda à IA para modelar um grafo

::prompt-snippet{title="Propor grafo a partir de perguntas do produto" model="Agente de código"}
Modele em SurrealDB um marketplace brasileiro de serviços locais.

Antes do schema, responda:
1. quais perguntas do produto realmente exigem travessia de grafo?
2. quais conexões devem ser record links simples?
3. quais relações carregam dados próprios?
4. onde precisamos de unicidade e idempotência?
5. quais dados pessoais exigem retenção e acesso restrito?

Depois entregue:
- nós e arestas;
- schema SCHEMAFULL;
- três consultas críticas;
- índices necessários;
- cinco testes negativos;
- riscos de performance.

Não use grafo apenas para demonstrar a tecnologia.
::

Esse último requisito é importante. Um grafo ruim apenas troca joins compreensíveis por setas difíceis de manter.

## Perguntas frequentes sobre grafos no SurrealDB

### SurrealDB é um banco de grafos?

Ele é multi-modelo e inclui recursos nativos de grafo. Isso permite combinar relações com documentos, consultas estruturadas e vetores no mesmo banco, sem limitar todo dado a uma única representação.

### Toda tabela de junção deve virar `RELATE`?

Não. Use relation table quando o vínculo tem significado próprio ou precisa ser percorrido como parte central do domínio. Uma referência simples pode continuar como campo de record.

### Grafo é mais rápido que join?

Não existe resposta universal. Desempenho depende de modelo, cardinalidade, índices, profundidade e padrão de consulta. Faça benchmark com as perguntas reais do produto.

### Posso combinar grafo e busca vetorial?

Sim. A busca vetorial encontra candidatos por significado; o grafo pode filtrar ou enriquecer por relações conhecidas. Esse é um dos padrões abordados em [RAG e agentes com SurrealDB](/blog/rag-agentes-surrealdb).

## Modele as perguntas, não o diagrama bonito

Liste primeiro as perguntas que movem o produto: quem pode acessar, o que recomendar, como a pessoa chegou, qual caminho falta concluir. Só depois escolha nós e arestas.

Se você ainda está decidindo o banco, veja [SurrealDB vs Postgres](/blog/surrealdb-vs-postgres). Se já escolheu, comece com uma relação que tenha valor claro, dados próprios e um teste de unicidade.

**Do vibe ao produto. Com método.**
::