Connectors: MongoDB e Neo4j

Índice


O que é

Os connectors são serviços dedicados que centralizam o acesso aos bancos de dados de todo o sistema. Anteriormente, a lógica de persistência estava distribuída entre os serviços da pipeline (processors), o que gerava duplicação de código e lógicas de persistências que durante a evolução do sistema se tornaram causas de bugs, por exemplo:

  • Cada serviço tinha uma lógica de geração do hash de deduplicação causando a inserção de nós e relações (HAS_SKILL e REQUIRES_SKILL) duplicadas no Neo4j,
  • Número de conexões crescendo conforme a adição de novos serviços na pipeline,
  • Dificuldade de manter a integridade dos dados e a evolução do schema.

A solução adotada (ADR-033) define dois connectors independentes:

ServiçoResponsabilidade
connector-mongodbDados brutos, processados e de treinamento
connector-neo4jGrafo de conhecimento: habilidades, currículos e oportunidades

Ambos se comunicam com os demais serviços exclusivamente via RabbitMQ, seguindo o padrão RPC over Message Queue para leituras síncronas e Fire-and-forget para escritas assíncronas.


Como Funciona

Padrão de Comunicação

Escritas: Fire-and-Forget (Assíncrono)

Para operações que não exigem retorno imediato, o produtor envia um evento e segue sua execução sem aguardar confirmação do banco:

O UUID da entidade é gerado pelo produtor (client-side UUID) antes de publicar o evento. Isso permite que o serviço referencie a entidade em etapas posteriores sem aguardar confirmação do banco.

Leituras: RPC com Direct Reply-to (Síncrono)

Para consultas que exigem o resultado de uma query (por exemplo, quando a API precisa do perfil de um usuário para prosseguir com a requisição), utiliza-se o padrão Direct Reply-to do RabbitMQ:

Cada chamada RPC inclui um request_id único e possui um TTL configurado para expiração automática em caso de indisponibilidade do connector, o que evita bloqueios indefinidos no processamento.


connector-mongodb

O connector-mongodb é o único serviço com driver PyMongo instalado. Ele gerencia as seguintes coleções, com responsabilidades separadas:

ColeçãoDomínioConteúdoEvento de Origem
raw_opportunitiesOportunidadesDados brutos do scraperOpportunityScraped
processed_opportunitiesOportunidadesSkills extraídas + metadados da vagaOpportunitySkillLinked / OpportunitySkillCustom
llm_training_data-Histórico de extrações (auditoria e fine-tuning)OpportunitySkillLinked / OpportunitySkillCustom / ResumeSkillLinked / ResumeSkillCustom
raw_resumesCurrículosTexto bruto submetido pelo usuário ou carregadoResumeUploaded / ResumeRawLoaded
processed_resumesCurrículosSkills extraídas + metadados do candidatoResumeSkillLinked / ResumeSkillCustom
profilesCandidatosDados cadastrais, onboarding e currículo vinculadoProfileRegister / ProfileUpdateResume / ProfileCompleteOnboarding
entity_linking-Detalhes de análise e convergência de Entity LinkingEntityLinkingAnalysisRecorded

Deduplicação via SHA-256

A fim de evitar a duplicação de conteúdo bruto no banco de dados, o connector calcula e armazena o hash SHA-256 dos campos da entidade (exceto campos de data e hora).

O hash é armazenado no campo hash. O connector utiliza o identificador da entidade (_id = entity_id) em conjunto com a operação $setOnInsert do PyMongo para garantir que o hash e os dados brutos sejam persistidos apenas no primeiro registro, descartando duplicatas sem gerar exceções.


connector-neo4j

O connector-neo4j é o único serviço com driver neo4j-driver Python instalado. Ele gerencia quatro tipos de nós no grafo de conhecimento:

Chave de IdentidadeChave de VersãoPropriedades Principais
(:Resume)profile_ididid, profile_id, knowledge_area, source
(:Opportunity)id-id, title, company, source
(:Skill)uri (ESCO URI)-uri, name, type, embedding
(:CustomSkill)name-name, type

Deduplicação

O connector utiliza a instrução MERGE em todos os nós para garantir a idempotência. Constraints de unicidade são aplicadas no Neo4j durante a inicialização do serviço:

CREATE CONSTRAINT resume_id IF NOT EXISTS
FOR (r:Resume) REQUIRE r.id IS UNIQUE;

CREATE CONSTRAINT skill_uri IF NOT EXISTS
FOR (s:Skill) REQUIRE s.uri IS UNIQUE;

CREATE CONSTRAINT custom_skill_name IF NOT EXISTS
FOR (cs:CustomSkill) REQUIRE cs.name IS UNIQUE;

CREATE CONSTRAINT opportunity_id IF NOT EXISTS
FOR (o:Opportunity) REQUIRE o.id IS UNIQUE;

A persistência de currículos requer tratamento específico para lidar com a submissão de múltiplos currículos pelo mesmo usuário ao longo do tempo. O sistema executa a instrução MERGE na chave profile_id e remove os relacionamentos HAS_SKILL anteriores se uma nova versão (novo id) for identificada:

// Se a versão mudou, limpa as relações antigas antes de atualizar
OPTIONAL MATCH (existing:Resume {profile_id: $merge_value})
WHERE existing.id IS NOT NULL AND existing.id <> $version_value
OPTIONAL MATCH (existing)-[r:HAS_SKILL]->()
DELETE r

// Merge e atualização atômica com timestamps
WITH count(*) as dummy
MERGE (n:Resume {profile_id: $merge_value})
ON CREATE SET n.created_at = datetime()
ON MATCH SET n.updated_at = datetime()
SET n += $properties

O fluxo garante que o nó (:Resume) de um usuário sempre reflita apenas as competências do último currículo processado.


ADR's Relacionadas

ADRDataDecisão
ADR-004Dez 2025Estratégia de deduplicação por SHA-256 no MongoDB
ADR-033Abr 2026Centralização e isolamento da lógica de banco em connectors via MQ
ADR-039Abr 2026Estratégia de deduplicação de currículos com limpeza de versão no Neo4j