Ir para o conteúdo

Schema do banco de dados

O corpus do IRIS Core é SQL plano (sem ORM): as migrações em ../db/migrations/ são a única fonte da verdade. Esta página é uma referência legível por pessoas gerada a partir dessas migrações. O corpus é neutro — zero campos RMC ou referências cruzadas (imposto por pnpm neutrality).

  • Engine: PostgreSQL 16 + pgvector (halfvec + HNSW), pg_trgm, fuzzystrmatch, unaccent, busca full-text em espanhol.
  • Ferramenta de migração: dbmate (cada arquivo tem -- migrate:up / -- migrate:down).

Diagrama entidade–relacionamento

erDiagram
    instancia ||--o{ instancia_clase : "classes"
    instancia ||--o{ instancia_persona : "owners/agents"
    instancia ||--o{ anotacion : "events"
    instancia |o--o| instancia : "renewal chain"
    persona ||--o{ instancia_persona : "role"
    clase ||--o{ instancia_clase : ""
    estado ||--o{ instancia : "status"
    tipo_signo ||--o{ instancia : "sign type"
    tipo_anotacion ||--o{ anotacion : "type"
    pais ||--o{ persona : "country"
    raw_source ||--o{ dead_letter : "re-derive handle"
    consumer ||--o{ api_key : "owns"

Corpus (entidades neutras)

instancia — a solicitação de marca

A entidade central. Chave natural nro_solicitud (D-01); denominacion é um atributo, nunca uma chave (D-02). Enriquecida de forma autoritativa pelo Buscador; semeada (somente nulls) pelo Sheets.

Coluna Tipo Notas
nro_solicitud text PK — chave natural
nro_registro text aparece somente na concessão, nullable
denominacion text NOT NULL — o texto da marca
tipo_signo text tipo_signo(codigo)
estado text estado(codigo)
tipo_nombre, subtipo_nombre text tipo/subtipo do INAPI (D-14)
fecha_presentacion, fecha_publicacion, fecha_registro, fecha_vencimiento date as quatro datas explícitas (D-14)
traduccion, descripcion_etiqueta, protection_description, imagen_url text atributos de detalhe (D-14)
renovada_de, renovada_por text auto-FK → instancia(nro_solicitud) — cadeia de renovação (D-17)
embedding halfvec(1024) vetor semântico, índice HNSW
search_vector tsvector gerada — FTS em espanhol sobre unaccent(denominacion)
content_hash text idempotência (INGEST-05)
created_at, updated_at timestamptz

persona — titulares e agentes

Coluna Tipo Notas
id bigint PK (identity)
pais text pais(codigo)
identificador text RUT (Chile) ou id estrangeiro, nullable
nombre, apellido text
region, comuna text opcional (D-16)
search_vector tsvector gerada, FTS
created_at timestamptz

Chave única uq_persona_pais_identificador (pais, identificador) onde identificador IS NOT NULL; personas sem id são reconciliadas por melhor esforço via (pais, lower(nombre)).

instancia_persona — ponte de papéis

Coluna Tipo Notas
instancia_nro_solicitud text PK, → instancia
persona_id bigint PK, → persona
rol text titular | representante | ambas

instancia_clase — classes de Nice

Coluna Tipo Notas
instancia_nro_solicitud text PK, → instancia
clase_numero int PK, → clase (1..45)
descripcion, estado text descrição/status por classe

anotacion — eventos do Estado-Diário (append-only)

Coluna Tipo Notas
id bigint PK (identity)
instancia_nro_solicitud text instancia (um pai ausente é convertido em stub/colocado em quarentena)
tipo text tipo_anotacion(codigo) — M1..M14
fecha, fecha_vencimiento date
observacion, seccion, seccion_nombre text
created_at timestamptz

Chave de dedup uq_anotacion_event (instancia_nro_solicitud, tipo, fecha, md5(coalesce(observacion,''))) — um evento repetido é um DO NOTHING limpo (eventos nunca sofrem mutação).


Catálogos

Tabela PK Outros
estado codigo nombre — conjunto controlado de status
tipo_signo codigo nombre
tipo_anotacion codigo seccion_nombre — M1..M14
pais codigo nombre — conjunto ISO completo + códigos descontinuados
clase numero nombre — classes de Nice, CHECK (numero BETWEEN 1 AND 45)

Um valor de catálogo desconhecido é aceito como null + registrado em log (D-09) — um novo valor do INAPI nunca interrompe a ingestão.


Ingestão / operações

Tabela Chave Finalidade
sync_state source (PK) cursor de retomada por fonte (cursor jsonb)
movement_log id feed de mudanças append-only (entity_type, entity_id, change_type, source, occurred_at, content_hash) — alimenta a lista de trabalho do Buscador
raw_source id bytes brutos retidos (storage_path sob RAW_STORAGE_DIR, content_type, size) — re-derivabilidade (D-10)
dead_letter id quarentena (source, reason, raw_source_idraw_source, payload jsonb) — não abortante (D-07)
sync_run id histórico de execução (source, started_at, finished_at, status ok\|failed\|running, processed, changed, quarantined, error) — OBS-03

Controle de acesso

Tabela Chave Finalidade
consumer id org/tenant (name UNIQUE)
api_key id consumer_idconsumer; key_hash (sha256, UNIQUE — nunca em texto puro), prefix, scopes text[], monthly_quota int (>= 0), expires_at, rotated_at, revoked_at

Escopos: brands:read (/v1/brands, MCP search_brands/get_brand_detail), insights:read (/v1/freshness, /v1/sync-runs). Escopos vazios = irrestrito. Os contadores de cota vivem no Redis (compartilhado REST ↔ MCP), não no Postgres, para que o caminho de leitura permaneça SELECT-only.


Índices principais

  • FTS: GIN em instancia.search_vector e persona.search_vector (espanhol, insensível a acentos via unaccent).
  • Fuzzy: pg_trgm GIN/GiST sobre a denominação para busca tolerante a erros de digitação.
  • Vetor: HNSW em instancia.embedding (halfvec, cosseno) para similaridade semântica.
  • Reconciliação: parcial idx_persona_noident_reconcile (pais, lower(nombre)) WHERE identificador IS NULL.
  • Chaves naturais / dedup: uq_anotacion_event, uq_persona_pais_identificador, além das PKs de tabela acima.

As migrações são aplicadas via dbmate up; o DDL completo (índices, colunas geradas, ordenação de FK, config de FTS em espanhol) vive em ../db/migrations/.