Skip to content

PAV-124: [Backend] Implementar filtros avançados e contrato de famílias na API - #291

Merged
Benevanio merged 2 commits into
masterfrom
develop
Oct 7, 2026
Merged

Benevanio merged 2 commits into
masterfrom
develop

Conversation

@hltav

@hltav hltav commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Linear

PAV-124 — [Backend] Implementar filtros avançados e contrato de famílias na API

Objetivo

Implementar no backend um contrato completo e documentado para filtros por família profissional, utilizando a taxonomia canônica consolidada na PAV-123.

A entrega adiciona:

  • suporte a uma ou várias famílias;
  • suporte a múltiplas famílias por vírgula;
  • suporte à repetição do parâmetro family;
  • parâmetro familyMode=primary|any;
  • familyMode=any como padrão;
  • operação OR entre famílias;
  • operação AND entre famílias e os demais filtros;
  • validação e normalização centralizadas;
  • endpoint /jobs/filters/options;
  • respostas de erro estáveis;
  • documentação OpenAPI/Swagger;
  • testes unitários, de contrato e integração.

Resumo das alterações

Foi criado um fluxo centralizado para interpretação dos filtros por família.

A API agora aceita:

GET /jobs/search?family=backend

GET /jobs/search?family=backend,fullstack

GET /jobs/search?family=backend&family=fullstack

Os formatos múltiplos são normalizados para uma estrutura determinística e equivalente.

Também foi adicionado:

familyMode=primary|any

Comportamento padrão:

familyMode=any

Taxonomia

A implementação reutiliza a taxonomia canônica criada na PAV-123.

Famílias públicas válidas:

  • backend
  • frontend
  • fullstack
  • mobile
  • data
  • devops
  • platform
  • qa
  • security
  • product
  • product_design
  • software
  • leadership

O valor:

other

continua sendo interno e não pode ser utilizado como filtro público.

Labels como:

Backend
Full Stack
Dados e IA
Design de Produto

também não são aceitos como identificadores.

familyMode

Foram adicionados dois modos de busca:

any

Considera:

classification.primaryFamily
classification.relatedFamilies

A vaga corresponde quando qualquer família solicitada estiver como principal ou relacionada.

primary

Considera somente:

classification.primaryFamily

Uma vaga cuja família esteja apenas em relatedFamilies não corresponde nesse modo.

Múltiplas famílias

Famílias múltiplas utilizam operação lógica OR.

Exemplo:

family=backend,fullstack&familyMode=primary

Semântica:

primaryFamily = backend
OR
primaryFamily = fullstack

Com familyMode=any, cada família pode corresponder à principal ou às relacionadas.

Combinação com outros filtros

O grupo de famílias utiliza AND em relação aos demais filtros existentes.

Exemplo:

family=backend,fullstack
familyMode=any
seniority=senior
modality=remote

Semântica:

(backend OR fullstack)
AND senior
AND remote

Foram preservados os filtros existentes de:

  • senioridade;
  • modalidade;
  • localização;
  • contrato;
  • texto;
  • provider;
  • tecnologias;
  • paginação;
  • ordenação;
  • demais filtros já suportados.

Parser centralizado

Foi adicionado um parser específico para family e familyMode.

O parser:

  • aceita string única;
  • aceita lista separada por vírgula;
  • aceita parâmetro repetido;
  • combina os formatos;
  • remove espaços;
  • remove valores vazios;
  • remove duplicidades;
  • valida contra a taxonomia canônica;
  • aplica ordenação determinística;
  • aplica familyMode=any como padrão;
  • retorna estrutura tipada.

Estrutura normalizada:

{
  families: JobFamily[];
  familyMode: "primary" | "any";
}

Ordem determinística

A ordem enviada pelo cliente não altera o resultado normalizado.

Exemplos equivalentes:

?family=backend,fullstack

?family=fullstack,backend

?family=backend&family=fullstack

Todos resultam na mesma estrutura normalizada.

Full Stack

fullstack continua sendo uma família própria.

A busca:

?family=fullstack&familyMode=primary

não é transformada automaticamente em:

backend OR frontend

Para consultar as três famílias, o cliente deve enviar explicitamente:

?family=backend,frontend,fullstack

DevOps e Platform

devops e platform permanecem famílias independentes.

A busca:

?family=devops&familyMode=primary

não inclui automaticamente vagas primárias de platform.

Para consultar ambas:

?family=devops,platform

Validação e erros

Famílias inválidas retornam:

400 Bad Request

Código:

INVALID_JOB_FAMILY

Valores inválidos não são:

  • ignorados;
  • convertidos para other;
  • parcialmente aceitos;
  • tratados com fallback silencioso.

familyMode inválido também retorna:

400 Bad Request

Código:

INVALID_FAMILY_MODE

O envelope de erro existente no backend foi preservado.

Camada de consulta

Foi criada uma camada de repository dedicada para aplicar corretamente a semântica de famílias antes da paginação final.

A implementação garante que:

  • página e total utilizem o mesmo conjunto de filtros;
  • não exista pós-filtro incorreto somente na página retornada;
  • o total reflita os mesmos critérios de busca;
  • o controller não contenha regra de negócio;
  • o service não reinterprete valores já normalizados;
  • a camada de consulta receba a semântica explícita.

Até a implementação dos novos índices da próxima PAV, a busca utiliza uma estratégia funcionalmente correta baseada na persistência atual.

Limitação temporária

Nesta entrega, a busca por famílias ainda possui custo linear em relação aos candidatos avaliados.

Essa decisão é temporária e deliberada.

A otimização ficará para a próxima sub-issue de Valkey, responsável por:

  • novos índices por família;
  • cache determinístico;
  • invalidação;
  • otimização de match score.

Também permanece a limitação de não existir snapshot transacional entre múltiplas leituras do Valkey.

Esses pontos não alteram a correção funcional do contrato implementado nesta PAV.

Endpoint de opções

Foi adicionado:

GET /jobs/filters/options

O endpoint retorna a taxonomia oficial para consumo dos clientes.

A resposta inclui:

taxonomyVersion
families
familyModes

Os modos expostos são:

any
primary

Com any marcado como padrão.

O endpoint:

  • utiliza a taxonomia canônica;
  • não inclui other;
  • possui ordem determinística;
  • não consulta todas as vagas;
  • não executa contagens pesadas;
  • não depende do Jobs Processor em execução;
  • permite cache HTTP;
  • possui ETag associado à taxonomia.

Cache HTTP

O endpoint de opções utiliza headers de cache HTTP.

A estratégia permite reutilização do contrato estático sem consultas desnecessárias ao backend.

OpenAPI / Swagger

A documentação foi atualizada para incluir:

  • parâmetro family;
  • múltiplos valores;
  • formato separado por vírgula;
  • parâmetro repetido;
  • familyMode;
  • valor padrão any;
  • operação OR;
  • combinação AND;
  • famílias válidas;
  • comportamento de Full Stack;
  • diferença entre DevOps e Platform;
  • respostas 400;
  • endpoint /jobs/filters/options;
  • exemplos de requisição e resposta.

O arquivo do Swagger também foi reformatado para melhorar legibilidade, sem alteração funcional correspondente a todo o volume exibido no diff.

Compatibilidade

O contrato antigo continua válido:

GET /jobs/search?family=backend

O comportamento padrão any preserva a semântica anterior observada no backend, que já considerava famílias principais e relacionadas.

Também foram preservados:

  • formato geral da resposta;
  • paginação;
  • filtros existentes;
  • autenticação;
  • rate limit;
  • campos atuais;
  • nomes das famílias.

Arquivos e módulos alterados

Backend

Principais arquivos:

backend/src/lib/errors.ts
backend/src/modules/jobs/controllers/jobFilterOptions.controller.ts
backend/src/modules/jobs/controllers/searchJobs.controller.ts
backend/src/modules/jobs/filters/jobSearch.filter.ts
backend/src/modules/jobs/parsers/familyQuery.parser.ts
backend/src/modules/jobs/parsers/jobSearchQuery.parser.ts
backend/src/modules/jobs/repositories/jobSearch.repository.ts
backend/src/modules/jobs/services/searchJobs.service.ts
backend/src/modules/jobs/types/jobSearch.types.ts
backend/src/routes/jobs.routes.ts
backend/src/swagger.ts

Testes

Foram adicionados e atualizados testes em:

backend/tests/integration/routes/searchJobs.routes.test.ts
backend/tests/unit/app.test.ts
backend/tests/unit/modules/jobs/familyQuery.parser.test.ts
backend/tests/unit/modules/jobs/jobSearch.filter.test.ts
backend/tests/unit/modules/jobs/jobSearch.repository.test.ts
backend/tests/unit/modules/jobs/searchJobs.service.test.ts
backend/tests/unit/swagger.test.ts

Documentação

Atualizado:

BACKEND.md

Git

O .gitignore foi atualizado para ignorar o relatório local:

PAV-124_REPORT.md

Fora do escopo

Esta alteração não inclui:

  • novos índices principais e relacionados no Valkey;
  • reconstrução completa dos índices;
  • cache keys finais;
  • invalidação de cache por reclassificação;
  • alterações no match score;
  • métricas Prometheus completas;
  • dashboard administrativo;
  • alterações em frontend/**;
  • alterações em front_admin/**;
  • alterações no Jobs Processor;
  • alterações no package-lock.json;
  • mudanças de comunicação Node ↔ Go;
  • introdução de gRPC.

Validação realizada

Foram executados:

npm test -- --run tests/unit tests/integration

Resultado:

756 testes aprovados
73 arquivos de teste

Também passaram:

TypeScript typecheck
validação da estrutura OpenAPI
git diff --check

O lint não cobre atualmente o backend pela configuração existente do projeto.

Segurança e desempenho

A implementação:

  • valida os parâmetros antes da consulta;
  • preserva autenticação;
  • preserva rate limiting;
  • evita interpolação insegura de parâmetros;
  • não introduz regex controlada pelo usuário;
  • não carrega todas as vagas retornadas em memória para depois paginar;
  • mantém página e total consistentes.

Riscos

O principal risco atual é de desempenho em bases maiores, devido ao custo linear temporário da estratégia utilizada antes da criação dos novos índices de família no Valkey.

Esse risco já está isolado para a próxima PAV de otimização.

Não foi introduzida alteração destrutiva de dados.

Rollback

O rollback pode ser feito revertendo os commits desta PR.

Não existem migrações destrutivas associadas a esta entrega.

Checklist

  • Suporte a uma família
  • Suporte a múltiplas famílias
  • Formato por vírgula
  • Parâmetro repetido
  • Parser centralizado
  • Remoção de duplicidades
  • Ordem determinística
  • familyMode=primary
  • familyMode=any
  • any como modo padrão
  • OR entre famílias
  • AND com demais filtros
  • Full Stack preservado como família própria
  • DevOps e Platform independentes
  • Família inválida retorna 400
  • familyMode inválido retorna 400
  • other não é público
  • Página e total usam os mesmos filtros
  • Endpoint /jobs/filters/options
  • 13 famílias canônicas
  • Cache HTTP
  • ETag
  • OpenAPI / Swagger atualizado
  • Testes unitários
  • Testes de integração
  • 756 testes aprovados
  • Typecheck aprovado
  • git diff --check
  • Nenhuma alteração em frontend/**
  • Nenhuma alteração em front_admin/**
  • Nenhuma alteração em scraper-go/**
  • Nenhuma alteração em package-lock.json

hltav and others added 2 commits October 7, 2026 07:59
…as na API (#289)

## Linear

[PAV-124 — [Backend] Implementar filtros avançados e contrato de
famílias na API](https://linear.app/candidateappbr/issue/PAV-124)

## Objetivo

Implementar no backend um contrato completo e documentado para filtros
por família profissional, utilizando a taxonomia canônica consolidada na
PAV-123.

A entrega adiciona:

- suporte a uma ou várias famílias;
- suporte a múltiplas famílias por vírgula;
- suporte à repetição do parâmetro `family`;
- parâmetro `familyMode=primary|any`;
- `familyMode=any` como padrão;
- operação `OR` entre famílias;
- operação `AND` entre famílias e os demais filtros;
- validação e normalização centralizadas;
- endpoint `/jobs/filters/options`;
- respostas de erro estáveis;
- documentação OpenAPI/Swagger;
- testes unitários, de contrato e integração.

## Resumo das alterações

Foi criado um fluxo centralizado para interpretação dos filtros por
família.

A API agora aceita:

    GET /jobs/search?family=backend

    GET /jobs/search?family=backend,fullstack

    GET /jobs/search?family=backend&family=fullstack

Os formatos múltiplos são normalizados para uma estrutura determinística
e equivalente.

Também foi adicionado:

    familyMode=primary|any

Comportamento padrão:

    familyMode=any

## Taxonomia

A implementação reutiliza a taxonomia canônica criada na PAV-123.

Famílias públicas válidas:

- `backend`
- `frontend`
- `fullstack`
- `mobile`
- `data`
- `devops`
- `platform`
- `qa`
- `security`
- `product`
- `product_design`
- `software`
- `leadership`

O valor:

    other

continua sendo interno e não pode ser utilizado como filtro público.

Labels como:

    Backend
    Full Stack
    Dados e IA
    Design de Produto

também não são aceitos como identificadores.

## `familyMode`

Foram adicionados dois modos de busca:

### `any`

Considera:

    classification.primaryFamily
    classification.relatedFamilies

A vaga corresponde quando qualquer família solicitada estiver como
principal ou relacionada.

### `primary`

Considera somente:

    classification.primaryFamily

Uma vaga cuja família esteja apenas em `relatedFamilies` não corresponde
nesse modo.

## Múltiplas famílias

Famílias múltiplas utilizam operação lógica `OR`.

Exemplo:

    family=backend,fullstack&familyMode=primary

Semântica:

    primaryFamily = backend
    OR
    primaryFamily = fullstack

Com `familyMode=any`, cada família pode corresponder à principal ou às
relacionadas.

## Combinação com outros filtros

O grupo de famílias utiliza `AND` em relação aos demais filtros
existentes.

Exemplo:

    family=backend,fullstack
    familyMode=any
    seniority=senior
    modality=remote

Semântica:

    (backend OR fullstack)
    AND senior
    AND remote

Foram preservados os filtros existentes de:

- senioridade;
- modalidade;
- localização;
- contrato;
- texto;
- provider;
- tecnologias;
- paginação;
- ordenação;
- demais filtros já suportados.

## Parser centralizado

Foi adicionado um parser específico para `family` e `familyMode`.

O parser:

- aceita string única;
- aceita lista separada por vírgula;
- aceita parâmetro repetido;
- combina os formatos;
- remove espaços;
- remove valores vazios;
- remove duplicidades;
- valida contra a taxonomia canônica;
- aplica ordenação determinística;
- aplica `familyMode=any` como padrão;
- retorna estrutura tipada.

Estrutura normalizada:

    {
      families: JobFamily[];
      familyMode: "primary" | "any";
    }

## Ordem determinística

A ordem enviada pelo cliente não altera o resultado normalizado.

Exemplos equivalentes:

    ?family=backend,fullstack

    ?family=fullstack,backend

    ?family=backend&family=fullstack

Todos resultam na mesma estrutura normalizada.

## Full Stack

`fullstack` continua sendo uma família própria.

A busca:

    ?family=fullstack&familyMode=primary

não é transformada automaticamente em:

    backend OR frontend

Para consultar as três famílias, o cliente deve enviar explicitamente:

    ?family=backend,frontend,fullstack

## DevOps e Platform

`devops` e `platform` permanecem famílias independentes.

A busca:

    ?family=devops&familyMode=primary

não inclui automaticamente vagas primárias de `platform`.

Para consultar ambas:

    ?family=devops,platform

## Validação e erros

Famílias inválidas retornam:

    400 Bad Request

Código:

    INVALID_JOB_FAMILY

Valores inválidos não são:

- ignorados;
- convertidos para `other`;
- parcialmente aceitos;
- tratados com fallback silencioso.

`familyMode` inválido também retorna:

    400 Bad Request

Código:

    INVALID_FAMILY_MODE

O envelope de erro existente no backend foi preservado.

## Camada de consulta

Foi criada uma camada de repository dedicada para aplicar corretamente a
semântica de famílias antes da paginação final.

A implementação garante que:

- página e total utilizem o mesmo conjunto de filtros;
- não exista pós-filtro incorreto somente na página retornada;
- o total reflita os mesmos critérios de busca;
- o controller não contenha regra de negócio;
- o service não reinterprete valores já normalizados;
- a camada de consulta receba a semântica explícita.

Até a implementação dos novos índices da próxima PAV, a busca utiliza
uma estratégia funcionalmente correta baseada na persistência atual.

## Limitação temporária

Nesta entrega, a busca por famílias ainda possui custo linear em relação
aos candidatos avaliados.

Essa decisão é temporária e deliberada.

A otimização ficará para a próxima sub-issue de Valkey, responsável por:

- novos índices por família;
- cache determinístico;
- invalidação;
- otimização de match score.

Também permanece a limitação de não existir snapshot transacional entre
múltiplas leituras do Valkey.

Esses pontos não alteram a correção funcional do contrato implementado
nesta PAV.

## Endpoint de opções

Foi adicionado:

    GET /jobs/filters/options

O endpoint retorna a taxonomia oficial para consumo dos clientes.

A resposta inclui:

    taxonomyVersion
    families
    familyModes

Os modos expostos são:

    any
    primary

Com `any` marcado como padrão.

O endpoint:

- utiliza a taxonomia canônica;
- não inclui `other`;
- possui ordem determinística;
- não consulta todas as vagas;
- não executa contagens pesadas;
- não depende do Jobs Processor em execução;
- permite cache HTTP;
- possui ETag associado à taxonomia.

## Cache HTTP

O endpoint de opções utiliza headers de cache HTTP.

A estratégia permite reutilização do contrato estático sem consultas
desnecessárias ao backend.

## OpenAPI / Swagger

A documentação foi atualizada para incluir:

- parâmetro `family`;
- múltiplos valores;
- formato separado por vírgula;
- parâmetro repetido;
- `familyMode`;
- valor padrão `any`;
- operação `OR`;
- combinação `AND`;
- famílias válidas;
- comportamento de Full Stack;
- diferença entre DevOps e Platform;
- respostas `400`;
- endpoint `/jobs/filters/options`;
- exemplos de requisição e resposta.

O arquivo do Swagger também foi reformatado para melhorar legibilidade,
sem alteração funcional correspondente a todo o volume exibido no diff.

## Compatibilidade

O contrato antigo continua válido:

    GET /jobs/search?family=backend

O comportamento padrão `any` preserva a semântica anterior observada no
backend, que já considerava famílias principais e relacionadas.

Também foram preservados:

- formato geral da resposta;
- paginação;
- filtros existentes;
- autenticação;
- rate limit;
- campos atuais;
- nomes das famílias.

## Arquivos e módulos alterados

### Backend

Principais arquivos:

    backend/src/lib/errors.ts
    backend/src/modules/jobs/controllers/jobFilterOptions.controller.ts
    backend/src/modules/jobs/controllers/searchJobs.controller.ts
    backend/src/modules/jobs/filters/jobSearch.filter.ts
    backend/src/modules/jobs/parsers/familyQuery.parser.ts
    backend/src/modules/jobs/parsers/jobSearchQuery.parser.ts
    backend/src/modules/jobs/repositories/jobSearch.repository.ts
    backend/src/modules/jobs/services/searchJobs.service.ts
    backend/src/modules/jobs/types/jobSearch.types.ts
    backend/src/routes/jobs.routes.ts
    backend/src/swagger.ts

### Testes

Foram adicionados e atualizados testes em:

    backend/tests/integration/routes/searchJobs.routes.test.ts
    backend/tests/unit/app.test.ts
    backend/tests/unit/modules/jobs/familyQuery.parser.test.ts
    backend/tests/unit/modules/jobs/jobSearch.filter.test.ts
    backend/tests/unit/modules/jobs/jobSearch.repository.test.ts
    backend/tests/unit/modules/jobs/searchJobs.service.test.ts
    backend/tests/unit/swagger.test.ts

### Documentação

Atualizado:

    BACKEND.md

### Git

O `.gitignore` foi atualizado para ignorar o relatório local:

    PAV-124_REPORT.md

## Fora do escopo

Esta alteração não inclui:

- novos índices principais e relacionados no Valkey;
- reconstrução completa dos índices;
- cache keys finais;
- invalidação de cache por reclassificação;
- alterações no match score;
- métricas Prometheus completas;
- dashboard administrativo;
- alterações em `frontend/**`;
- alterações em `front_admin/**`;
- alterações no Jobs Processor;
- alterações no `package-lock.json`;
- mudanças de comunicação Node ↔ Go;
- introdução de gRPC.

## Validação realizada

Foram executados:

    npm test -- --run tests/unit tests/integration

Resultado:

    756 testes aprovados
    73 arquivos de teste

Também passaram:

    TypeScript typecheck
    validação da estrutura OpenAPI
    git diff --check

O lint não cobre atualmente o backend pela configuração existente do
projeto.

## Segurança e desempenho

A implementação:

- valida os parâmetros antes da consulta;
- preserva autenticação;
- preserva rate limiting;
- evita interpolação insegura de parâmetros;
- não introduz regex controlada pelo usuário;
- não carrega todas as vagas retornadas em memória para depois paginar;
- mantém página e total consistentes.

## Riscos

O principal risco atual é de desempenho em bases maiores, devido ao
custo linear temporário da estratégia utilizada antes da criação dos
novos índices de família no Valkey.

Esse risco já está isolado para a próxima PAV de otimização.

Não foi introduzida alteração destrutiva de dados.

## Rollback

O rollback pode ser feito revertendo os commits desta PR.

Não existem migrações destrutivas associadas a esta entrega.

## Checklist

- [x] Suporte a uma família
- [x] Suporte a múltiplas famílias
- [x] Formato por vírgula
- [x] Parâmetro repetido
- [x] Parser centralizado
- [x] Remoção de duplicidades
- [x] Ordem determinística
- [x] `familyMode=primary`
- [x] `familyMode=any`
- [x] `any` como modo padrão
- [x] OR entre famílias
- [x] AND com demais filtros
- [x] Full Stack preservado como família própria
- [x] DevOps e Platform independentes
- [x] Família inválida retorna 400
- [x] `familyMode` inválido retorna 400
- [x] `other` não é público
- [x] Página e total usam os mesmos filtros
- [x] Endpoint `/jobs/filters/options`
- [x] 13 famílias canônicas
- [x] Cache HTTP
- [x] ETag
- [x] OpenAPI / Swagger atualizado
- [x] Testes unitários
- [x] Testes de integração
- [x] 756 testes aprovados
- [x] Typecheck aprovado
- [x] `git diff --check`
- [x] Nenhuma alteração em `frontend/**`
- [x] Nenhuma alteração em `front_admin/**`
- [x] Nenhuma alteração em `scraper-go/**`
- [x] Nenhuma alteração em `package-lock.json`
@Benevanio
Benevanio merged commit 28f83d4 into master Oct 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants