> ## Documentation Index
> Fetch the complete documentation index at: https://strattumai.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Padrões e armadilhas

> A regra de ouro do er_fields, o teste de uma pergunta, os seis padrões, as armadilhas que já aconteceram de verdade e o checklist antes de ativar.

Esta página é a última antes de ativar. Ela concentra o que dá errado e como evitar.

***

## A regra de ouro

**`er_fields` é OU, nunca E.**

`[cpf, email]` significa *"acha por CPF **ou** por e-mail"*. Não existe chave composta — cada campo é uma **porta independente** para a mesma entidade, e basta uma abrir.

```text theme={null}
linha 1:  {id: CRM-001, cpf: 111.222.333-44, email: ana@x.com}
          → grava cpf 11122233344 → entidade A
          → grava email ana@x.com → entidade A

linha 3:  {id: MKT-9, cpf: 999.888.777-66, email: ana@x.com}
          → cpf não achou
          → email ACHOU a entidade A → adota
```

CPF completamente diferente, e mesmo assim virou a mesma pessoa. **Não existe votação.**

A consequência que morde: troque `ana@x.com` por `contato@empresa.com.br` — uma caixa de setor — e todo mundo que a compartilha vira uma pessoa só.

<Warning>
  **A força da sua identidade é o campo mais FRACO da lista, não o mais forte.** Cada campo novo é mais uma maneira de errar, e o mais permissivo é quem manda.
</Warning>

### E se eu quiser a combinação?

Não dá na ontologia. Fabrique a coluna composta na camada clean e declare ela:

```sql theme={null}
cpf || '|' || lower(email)  AS cpf_email
```

```yaml theme={null}
er_fields: [cpf_email]
```

***

## O teste de uma pergunta

Antes de colocar um campo em `er_fields`:

> Se duas linhas tiverem o mesmo valor **neste campo**, elas são obrigatoriamente a mesma coisa no mundo real?

Se a resposta for *"não, só se o outro campo também bater"*, esse campo **não entra**.

Na dúvida, deixe de fora. Perder uma fusão é recuperável — basta declarar depois, e o lookup primário-primeiro garante que nada é re-chaveado. Colapsar mil entidades exige limpar o índice e reprocessar.

***

## Os seis padrões

1. **`id_field` é a chave que a fonte já tem e não muda** — normalmente `external_id`. Nunca um campo que o usuário edita.
2. **`er_fields` é só o que passa no teste** acima.
3. **`normalizers` quando o nome do campo não entrega a regra.** `cpf`, `cnpj`, `email`, `phone` e `telefone` são reconhecidos pelo nome; todo o resto cai em `text`.
4. **Fontes podem declarar listas diferentes** — cada uma declara só o que tem. O que não pode é o conjunto ficar desconexo.
5. **Sem elo em comum, não force.** Duas fontes sem identificador compartilhado não são uma entidade. Modele separado e resolva o dado na origem.
6. **Na aresta, `source:` é a tabela que carrega a ligação** — a que tem as duas colunas.

***

## Armadilhas que já aconteceram

### 1. Campo repetido em `er_fields`

Um gerador de teste produziu 20 000 veículos com só 9 000 placas distintas, porque o `lpad` truncava a partir de um certo índice. Resultado: **11 000 veículos fundiram em silêncio**, cada linha sobrescrevendo as propriedades da anterior, e o run reportou sucesso.

Hoje o worker avisa quando uma fonte colapsa acima de 20%, mas o modelo errado continua sendo de quem escreveu a ontologia.

### 2. Tratar `er_fields` como chave composta

Tentar casar reunião por "horário mais responsável":

```yaml theme={null}
er_fields: [meeting_at, owner_email]   # errado
```

Como é OU, isso funde **todas as reuniões do mesmo responsável numa só**.

### 3. Mesmo label, duas fontes, nenhum `er_fields`

Passa na validação e parece certo. Mas sem identificador declarado cada fonte cunha no seu próprio namespace, e você fica com **duas populações separadas usando o mesmo nome** — o card da ontologia mostra `Reunião (247)` e metade é de um conector, metade de outro, sem nenhuma aresta entre elas.

Aconteceu de verdade com `Meeting` vindo de um CRM e de uma ferramenta de gravação de calls.

### 4. Identificador sem o normalizador que precisa

Uma coluna chamada `documento` guardando CPF cai no padrão `text`, que **mantém a pontuação**. As quatro fontes escrevem quatro strings diferentes, viram quatro linhas no índice e **quatro entidades**. A busca depois acha uma delas, então o sintoma parece bug de busca.

O worker avisa:

```text theme={null}
Label Cliente: the identifier 'documento' has values that are the same once the
separators are removed but were keyed apart — '111.222.333-44' and '11122233344'.
[...] Declare the rule explicitly, e.g. normalizers: {documento: digits}
```

### 5. `digits` onde o identificador tem letra

O RG brasileiro pode terminar em **X**. Com `digits`:

```text theme={null}
12.345.678-X  →  12345678
1.234.567-8   →  12345678     ← RGs diferentes, a MESMA chave
```

Use **`alnum`** para qualquer identificador que possa conter letra: RG, placa Mercosul, chassi, códigos alfanuméricos de ERP.

### 6. Declarar `er_fields` só para "ficar pesquisável"

Se você só quer achar pelo valor do próprio `id_field`, **isso já é de graça** — e declarar tira o nó do caminho de ingestão mais rápido sem ganho nenhum. Ver [Campos pesquisáveis](/admin/ontology-searchable-fields).

***

## Checklist antes do apply

* [ ] cada campo de `er_fields` passa no teste da pergunta — **sozinho**
* [ ] cada campo de `er_fields` (ou a coluna do `identifier_columns`) está em `properties`
* [ ] as fontes do mesmo label formam uma cadeia conectada — nenhuma ilha
* [ ] campo com formato variável tem `normalizers` declarado
* [ ] identificador que pode ter letra usa `alnum`, não `digits`
* [ ] a tabela do `source:` de cada aresta tem **as duas** colunas de ligação
* [ ] nenhum campo de visualização — se você escreveu `display_*`, apague
* [ ] depois de rodar: a queda no número de entidades é **a que você esperava**

Uma queda maior que a prevista é colapso, não fusão.

***

## Ontologias que não funcionam

| O que alguém tenta                                     | O que acontece                                              |
| ------------------------------------------------------ | ----------------------------------------------------------- |
| duas fontes do mesmo label sem nada em comum           | erro no apply — não é uma entidade, são duas                |
| `er_fields` com coluna que não está em `properties`    | erro no apply — a coluna não é lida                         |
| `normalizers` num campo que não é identificador        | erro no apply — ficaria decorativo                          |
| `identifier_columns` apontando para coluna inexistente | erro no apply                                               |
| `match_field` numa coluna que o outro lado não tem     | a aresta não resolve; o contador `zero_match` sobe          |
| `er_fields: [status]`                                  | funde por categoria — tudo que é "aberto" vira uma entidade |

O que a plataforma **não** pega: dois identificadores legítimos que por acaso colidem, e um campo que é único hoje e deixa de ser amanhã. Isso é modelagem, e só quem conhece o negócio decide.

<CardGroup cols={2}>
  <Card title="Gerenciar a ontologia" icon="rotate" href="/admin/manage-ontology">
    Salvar, validar, ativar e reverter uma versão sem derrubar o grafo.
  </Card>

  <Card title="Conceitos da ontologia" icon="book" href="/admin/ontology-concepts">
    Voltar ao começo da trilha.
  </Card>
</CardGroup>
