MongoDB: Validação de esquema
A validação de esquema é a validação de dados na camada do banco de dados — ela pode filtrar dados inválidos mesmo sem uma camada de aplicação.
O valor único da validação no nível do banco de dados: Por que a validação de esquema ainda é necessária, mesmo com os validadores do Mongoose? Porque a validação do Mongoose só entra em vigor na camada de aplicação — 1. Integração entre múltiplas aplicações: Se vários microsserviços (Node.js/Python/Go) gravarem no mesmo banco de dados MongoDB, mas apenas o serviço Node.js utilizar a validação do Mongoose, enquanto os demais não; 2. Operações diretas no banco de dados: equipes de operações que usam o shell do Mongo para reparar dados ou scripts de ETL que gravam diretamente no banco de dados contornam o Mongoose; 3. Defesa em profundidade: mesmo que haja bugs na validação da camada de aplicação, a camada do banco de dados ainda pode interceptá-los. A validação de esquema serve como última linha de defesa — ela não é acionada em circunstâncias normais (já que a camada de aplicação já interceptou os problemas), mas protege a integridade dos dados em situações excepcionais.
Limitações e soluções alternativas para a validação de esquema: A validação de esquema do MongoDB apresenta limitações claras — 1. Ela não oferece suporte à validação entre campos (por exemplo, “endDate > startDate”), que deve ser tratada na camada de aplicação; 2. Não suporta validação assíncrona (por exemplo, “username deve ser único”, o que requer uma consulta ao banco de dados), que deve ser tratada por meio de um índice único; 3. Não suporta validação condicional (por exemplo, “author é obrigatório quando type='book'”), que deve ser tratada pela camada de aplicação; 4. $jsonSchema não suporta todos os operadores do MongoDB (por exemplo, $regex é restrito). Portanto, a validação de esquema não pode substituir totalmente a validação na camada de aplicação — a abordagem correta é que a camada de aplicação lide com a validação abrangente (mensagens de erro intuitivas, lógica entre campos, validação assíncrona), enquanto a camada de banco de dados lida com a validação de fallback (campos obrigatórios, tipos de dados, intervalos e exclusividade).
1. O que você vai aprender
- Validador $jsonSchema
- ação do validador (erro / aviso)
- nível de validação (rigoroso / moderado)
- Comparação entre o esquema do Mongoose e o $jsonSchema do MongoDB
- Estratégia de evolução do esquema
graph LR
A[Client-Side Document Insertion] --> B{mongo<br/>Schema Validation}
B -->|validationLevel<br/>strict/moderate| C{Validation Rules}
C -->|bsonType| D[Type Checking]
C -->|required| E[Required Checks]
C -->|pattern| F[Regular Expression Validation]
C -->|enum| G[Enumeration Check]
C -->|minLength| H[Length Check]
D --> I{Through?}
E --> I
F --> I
G --> I
H --> I
I -->|Yes + action=error| J[✅ Insertion successful]
I -->|No + action=error| K[❌ Reject + Throw error]
I -->|No + action=warn| L[⚠️ Allow + Warning]
style J fill:#d4edda
style K fill:#f8d7da
2. Validador $jsonSchema
Explicação do conceito: $jsonSchema é uma linguagem de validação de esquema de documentos introduzida no MongoDB 3.6+ e baseada na especificação JSON Schema. Ela permite definir regras estruturais que os documentos devem satisfazer no nível do banco de dados — tais como tipos de campos, campos obrigatórios, intervalos de valores e padrões de expressões regulares. Ao contrário da validação na camada de aplicação, o $jsonSchema é aplicado pelo mecanismo do MongoDB, e qualquer cliente (Python, Java, Node.js) que grave dados deve estar em conformidade com ele.
Como funciona: Ao criar uma coleção com um validador, o MongoDB armazena as regras $jsonSchema nos metadados da coleção. Para cada operação de inserção ou atualização, o mecanismo valida automaticamente se o documento atende às regras antes de gravá-lo. Se a validação falhar, o sistema determina se deve gerar um erro e rejeitar a operação ou registrar um aviso, com base na ação de validação (validationAction).
Palavras-chave principais do $jsonSchema:
| Palavra-chave | Função | Exemplo |
|---|---|---|
bsonType |
Especificar tipo BSON | 'string', 'int', 'object', 'array' |
required |
Lista de campos obrigatórios | ['email', 'username'] |
properties |
Definição de regras no nível do campo | { email: { bsonType: 'string' } } |
pattern |
Validação de expressões regulares | '^.+@.+$' (Formato de e-mail) |
enum |
Valor de enumeração | ['customer', 'admin'] |
minimum / maximum |
Faixa de valores | minimum: 0, maximum: 150 |
minLength / maxLength |
Comprimento da sequência | minLength: 3, maxLength: 30 |
items |
Regras para elementos de matriz | { bsonType: 'string' } |
minItems |
Comprimento mínimo da matriz | minItems: 1 |
A relação entre $jsonSchema e JSON Schema: O $jsonSchema do MongoDB é baseado na especificação JSON Schema Draft 4, mas há várias diferenças importantes — 1. Ele usa bsonType em vez de type (porque o JSON Schema não faz distinção entre tipos BSON, como int, double, decimal e objectId); 2. additionalProperties é definido como true por padrão (permitindo campos indefinidos, o que difere do padrão no JSON Schema Draft 4); 3. Ele não suporta referências $ref (todas as regras devem ser definidas inline); 4. Não suporta format (por exemplo, email, uri, date-time; em vez disso, devem ser usadas expressões regulares). Compreender essas diferenças ajuda a evitar a confusão de “copiar código de um tutorial do JSON Schema literalmente apenas para encontrar erros”.
Validação aninhada com $jsonSchema: O $jsonSchema suporta a validação recursiva de objetos e matrizes aninhados — 1. Para objetos aninhados, defina a subestrutura usando properties e required (por exemplo, address: {bsonType: 'object', required: ['city'], properties: {city: {bsonType: 'string'}}}); 2. Para matrizes, use items para definir regras de elementos (por exemplo, tags: {bsonType: 'array', items: {bsonType: 'string'}} para validar que todos os elementos da matriz sejam strings); 3. Não há limite estrito para a profundidade de aninhamento, mas o aninhamento excessivo pode afetar o desempenho da validação e a legibilidade — considere dividir em coleções separadas para mais de três níveis de aninhamento. A validação aninhada é uma vantagem fundamental do modelo de documento — enquanto o SQL requer JOINs entre várias tabelas para validar dados relacionados, o MongoDB valida toda a árvore do documento em uma única passagem.
Casos de uso:
- Proteção de estruturas de dados essenciais (usuários, pedidos, registros de pagamento)
- Validação unificada para bancos de dados compartilhados em microsserviços multilíngues
- Impedir que dados incorretos sejam gravados (como medida alternativa caso falhe a validação na camada de aplicação)
- Não é adequado para: validação que exija processamento assíncrono, validação em várias coleções ou validação que envolva lógica de negócios complexa
A relação entre a especificação $jsonSchema e o JSON Schema: O $jsonSchema do MongoDB é baseado na especificação JSON Schema Draft 4, mas inclui extensões do BSON — substituindo type por bsonType (já que o MongoDB usa BSON em vez de JSON para tipos) e adicionando tipos específicos do BSON, como objectId, decimal e date. É importante compreender essa relação: 1. bsonType: 'string' corresponde a type: 'string' do JSON Schema; 2. bsonType: 'int' não tem equivalente direto (o JSON possui apenas number); 3. required, properties, pattern e enum são idênticos aos do JSON Schema.
Estratégia de migração de versões: Alterações na validação do esquema exigem uma estratégia de controle de versões — 1. Adição de campos opcionais: Risco baixo; implantar diretamente com “moderado” + “aviso”; 2. Adição de campos obrigatórios: Risco médio; primeiro definir como “opcional” → realizar a migração de dados → depois definir como “obrigatório”; 3. Alteração de tipos de campo: alto risco; campos de gravação dupla → migrar → alternar → excluir campos antigos; 4. Restrição de intervalos de valores: risco médio — primeiro observar com “moderado” e “aviso” → confirmar que não há violações generalizadas → alternar para “erro”. Registrar as regras antigas para cada alteração; reverter usando collMod se necessário.
// === Create a collection with validation ===
db.createCollection('users', {
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['email', 'username'],
properties: {
email: {
bsonType: 'string',
pattern: '^.+@.+$',
maxLength: 100
},
username: {
bsonType: 'string',
minLength: 3,
maxLength: 30
},
age: {
bsonType: 'int',
minimum: 0,
maximum: 150
},
role: {
enum: ['customer', 'admin', 'moderator']
},
isActive: {
bsonType: 'bool'
}
}
}
},
validationLevel: 'strict',
validationAction: 'error'
});
Análise dos pontos-chave:
bsonTypeAo contrário do JSON Schematype, o MongoDB utiliza nomes de tipos BSON (como'int'em vez de'number')requiredé uma palavra-chave de nível superior cujo valor é uma matriz de nomes de campos; ela não pertence a nenhuma propriedade.- Os documentos aninhados são definidos usando
properties, e os elementos da matriz são definidos usandoitems
Padrões de projeto para validação aninhada: Existem três padrões de projeto para validação aninhada em $jsonSchema: 1. Padrão totalmente embutido (em que address e item são diretamente aninhados dentro do $jsonSchema do order; isso proporciona uma estrutura clara, mas resulta em código prolixo); 2. Padrão de extração de variáveis (em que addressSchema e itemSchema são definidos como variáveis JavaScript e referenciados no esquema principal; isso oferece boa reutilização de código, mas requer gerenciamento na camada de aplicação); 3. Modo híbrido (os campos principais são incorporados, enquanto as subestruturas reutilizáveis são extraídas como variáveis). O Modo 3 é recomendado para ambientes de produção — uma vez que endereços e itens de pedidos podem ser reutilizados em várias coleções (tanto pedidos quanto usuários possuem endereços), extraí-los como variáveis independentes reduz definições redundantes.
Casos extremos na validação de matrizes: Existem vários casos extremos propensos a erros na validação de matrizes do $jsonSchema — 1. minItems e maxItems verificam o comprimento da matriz em vez do número de documentos (uma matriz vazia [] é aprovada em minItems: 0, mas reprovada em minItems: 1); 2. items define as regras para todos os elementos (não suporta a validação de tuplas em que “os três primeiros elementos são de tipos diferentes”; o JSON Schema Draft 4 suporta isso, mas o MongoDB não); 3. uniqueItems: true verifica a exclusividade dos elementos da matriz, mas pode não funcionar como esperado com objetos aninhados (os objetos são comparados por referência, e não por profundidade); 4. Matriz vazia x matriz nula — uma matriz vazia [] é validada com bsonType: 'array', mas null não (requer bsonType: ['array', 'null'] para permitir null).
▶ Exemplo 1: Documentos aninhados + validação de matriz
// ShopHub Order Collection:Nested Addresses + Validation of the Order Items Array
db.createCollection('orders', {
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['userId', 'items', 'total', 'address'],
properties: {
userId: { bsonType: 'objectId' },
items: {
bsonType: 'array',
minItems: 1,
items: {
bsonType: 'object',
required: ['productId', 'qty', 'price'],
properties: {
productId: { bsonType: 'objectId' },
qty: { bsonType: 'int', minimum: 1 },
price: { bsonType: 'decimal', minimum: 0 }
}
}
},
address: {
bsonType: 'object',
required: ['street', 'city', 'zipCode'],
properties: {
street: { bsonType: 'string', minLength: 1 },
city: { bsonType: 'string' },
zipCode: { bsonType: 'string', pattern: '^[0-9]{5,10}$' }
}
},
total: { bsonType: 'decimal', minimum: 0 }
}
}
},
validationLevel: 'moderate',
validationAction: 'error'
});
// Test:Valid Orders
db.orders.insertOne({
userId: ObjectId(),
items: [{ productId: ObjectId(), qty: Int32(2), price: Decimal128('29.99') }],
address: { street: '123 Main St', city: 'Seattle', zipCode: '98101' },
total: Decimal128('59.98')
});
// ✅ Success
// Test: Empty items Array
db.orders.insertOne({
userId: ObjectId(),
items: [],
address: { street: '123 Main St', city: 'Seattle', zipCode: '98101' },
total: Decimal128('0')
});
// ❌ Document failed validation (minItems: 1)
3. validationAction
Descrição do conceito: validationAction Controla o comportamento do MongoDB quando uma verificação de validação falha — se a operação deve ser rejeitada de forma estrita (erro) ou permitida com um aviso (aviso). Trata-se de um equilíbrio crítico entre a integridade dos dados e a continuidade dos negócios.
Como funciona:
errorModo: Gera um erroDocumentFailedValidationquando a validação falha; a operação de gravação é revertida e o cliente recebe uma exceção.warnModo: as gravações continuam sendo bem-sucedidas mesmo que a validação falhe, mas uma mensagem de aviso é registrada no logmongod; adequado para implementação gradual
O valor operacional do modo “warn”: O principal valor do modo “warn” é a “implantação de novas regras sem risco” — ao adicionar uma nova regra de validação, defina-a primeiro como “warn”, monitore os logs por 1 a 2 semanas e acompanhe quantas operações de gravação existentes são rejeitadas. Se a taxa de violação for <1%, a regra é considerada segura e pode ser alterada para o modo error; se a taxa de violação for >5%, a regra precisa ser ajustada ou os dados históricos precisam ser limpos primeiro. Essa estratégia de implantação incremental evita incidentes de produção, como “erros generalizados imediatamente após a implantação”. Para consultar os logs warn, use db.adminCommand({getLog: 'global'}) e filtre pela palavra-chave DocumentFailedValidation.
Solução de monitoramento para logs “warn”: Os logs no modo “warn” exigem monitoramento proativo — 1. Filtragem de logs: os logs “warn” do MongoDB estão misturados com outros logs; após a coleta via Filebeat/Fluentd, filtre pela palavra-chave “DocumentFailedValidation”; 2. Regras de alerta: se o número de violações ultrapassar 10 por hora, acione um alerta no Slack ou por e-mail (indicando que a regra pode estar muito rígida); 3. Painel de violações: agrupe e conte as violações por coleção, campo e tipo de erro para determinar quais regras precisam de ajuste; 4. Relatórios automatizados: gere um relatório resumido diário das violações (Z violações no campo Y da coleção X) e envie-o às equipes de DBA e de back-end. O modo “aviso” não é uma abordagem do tipo “configure e esqueça”, mas sim do tipo “configure e monitore de perto” — somente por meio do monitoramento contínuo é possível mudar com segurança do modo “aviso” para o modo “erro”.
Casos de uso:
| Etapa | Ação recomendada | Motivo |
|---|---|---|
| Desenvolvimento/Testes | erro | Detectar problemas de dados antecipadamente |
| Fase inicial da implementação da nova regra | aviso | Evite interromper as operações comerciais; monitore as violações |
| Assim que as regras estiverem definidas | erro | Aplique-as para garantir a integridade dos dados |
| Migração de dados | desativado | Desativado temporariamente para evitar que dados antigos sejam rejeitados |
graph LR
A[Write Operation] --> B{Schema Validation}
B -->|Through| C[✅ Write successful]
B -->|Failure + action=error| D[❌ Throw Error, Reject]
B -->|Failure + action=warn| E[⚠️ Write successful + Log Warning]
style C fill:#d4edda
style D fill:#f8d7da
style E fill:#fff3cd
| ação | Comportamento | Cenários aplicáveis |
|---|---|---|
error |
Falha na inserção/atualização (exceção lançada) | Ambiente de produção — a integridade dos dados tem prioridade |
warn |
Permitido, mas registrar um aviso (não gerar um erro) | Implementação gradual, período de observação |
// === error Pattern(Recommended Production)===
db.createCollection('users', {
validator: { $jsonSchema: {...} },
validationAction: 'error'
});
// === warn Pattern (Lenient) ===
db.createCollection('users', {
validator: { $jsonSchema: {...} },
validationAction: 'warn'
});
// Inserting an Incompatible Document:Success + Warning Log
Análise dos pontos-chave:
- Antes de mudar de “aviso” para “erro”, recomendamos analisar primeiro a frequência das violações nos registros de “aviso”.
- Os registros no modo de aviso podem ser visualizados por meio do
db.adminCommand({getLog:'global'}) validationAction: 'off'não existe; para desativar a validação, definavalidationLevel: 'off'
4. nível de validação
Descrição do conceito: validationLevel determina a quais documentos as regras de validação se aplicam — apenas a novos documentos (moderado) ou incluindo documentos existentes (rigoroso). Essa é uma configuração essencial para a evolução do esquema e determina como as novas regras afetam os dados existentes.
Como funciona:
strict: Todas as instruções INSERT e UPDATE são validadas, inclusive ao modificar documentos existentes.moderate: A validação é realizada apenas em documentos recém-inseridos e em atualizações de documentos que já atendem às regras de validação; atualizações de documentos históricos que não atendem às regras não são validadas.off: Desativar completamente a validação
Casos de uso:
| Cenário | Nível recomendado | Motivo |
|---|---|---|
| Coleção totalmente nova | rigorosa | Sem histórico, cuidadosamente verificada |
| Novas regras para conjuntos existentes | moderado | Como evitar que dados antigos sejam atualizados |
| Migração de dados em andamento | desativado | Desativado temporariamente; será reativado após a conclusão da migração |
| Regras consistentes + Dados limpos | rigoroso | Proteção máxima |
| Nível | Comportamento | Cenários aplicáveis |
|---|---|---|
strict |
Validar todos os documentos (incluindo os já existentes) | Nova coleção, dados limpos |
moderate |
Verificar apenas documentos recém-inseridos/atualizados (recomendado) | Coleções existentes, implementação gradual |
off |
Sem verificação | Migração de dados |
// === moderate Pattern(Recommendations)===
db.createCollection('users', {
validator: { $jsonSchema: {...} },
validationLevel: 'moderate'
});
// Existing dirty data is not validated,Validate only new data
Análise dos pontos-chave:
- “moderado” é o nível mais comumente utilizado em ambientes de produção; ele não bloqueia atualizações em dados históricos marcados como “dirty”.
- Antes de mudar de “moderado” para “rigoroso”, é preciso primeiro corrigir os dados históricos que não estão em conformidade.
collModÉ possível modificar dinamicamente ovalidationLevelsem precisar reconstruir a coleção.
Riscos implícitos do modo “moderado”: A abordagem de “validar apenas novos dados” no modo “moderado” pode parecer segura, mas acarreta riscos implícitos — 1. Dados antigos podem ser atualizados um número ilimitado de vezes sem acionar a validação, de modo que dados incorretos podem ficar cada vez mais corrompidos; 2. O código do aplicativo pode depender de regras de esquema (como presumir que todos os documentos tenham um campo email), fazendo com que o aplicativo trave quando dados antigos não estiverem em conformidade; 3. “Moderado” não é um “padrão seguro”, mas sim um “período de carência para migração” — você deve mudar para “Rigoroso” assim que a migração estiver concluída. Prática recomendada: comece com “Moderado” + “Aviso” para monitorar a taxa de violação e mude para “Rigoroso” + “Erro” assim que a taxa de violação cair para 0.
Coordenação da migração e validação de dados: A validação do esquema e a migração de dados devem ser coordenadas — 1. Ao adicionar um novo campo obrigatório, defina primeiro um valor padrão (para evitar que documentos antigos fiquem sem os campos obrigatórios) e, em seguida, use um script de migração para preencher os dados históricos; 2. Ao adicionar uma restrição de enumeração, primeiro certifique-se de que todos os valores históricos estejam dentro do intervalo da enumeração (caso contrário, os dados antigos poderão ser atualizados no modo moderado, mas resultarão em um erro no modo estrito); 3. Ao tornar as restrições mais rígidas (por exemplo, alterando maxlength de 200 para 100), observe primeiro o comportamento no modo warn para confirmar que não há dados excessivamente longos antes de mudar para o modo error. Os scripts de migração normalmente utilizam bulkWrite + $set para atualizar em lote os dados históricos.
5. Modificação do validador para um conjunto existente
Explicação do conceito: Em um ambiente de produção, os esquemas não são estáticos — as iterações de negócios podem exigir a adição de novos campos, a modificação de regras ou até mesmo a remoção de restrições. O comando collMod permite modificar validadores de coleções existentes em tempo real, sem a necessidade de reconstruir a coleção ou tirar o serviço do ar.
Observações importantes sobre o collMod: O validador collMod realiza uma “substituição completa” em vez de uma “fusão incremental” — você deve fornecer o esquema $jsonSchema completo a cada chamada, mesmo que esteja alterando apenas um campo. Isso significa que: 1. Você deve salvar o esquema atual antes de fazer alterações (use db.getCollectionInfos() para visualizar o validador existente); 2. O novo esquema deve incluir todos os campos do esquema antigo (caso contrário, os campos não listados não serão mais validados); 3. Recomenda-se gerenciar as regras do $jsonSchema usando controle de versão (como o Git) para facilitar reversões e auditorias. Para remover um validador, use um objeto vazio { } com validationLevel: 'off', em vez de excluir o campo do validador.
Práticas de controle de versão de esquemas: As definições $jsonSchema devem ser incluídas no controle de versão — 1. Um arquivo JSON por coleção (por exemplo, validators/users.json, validators/orders.json), gerenciado no mesmo repositório que o código da aplicação; 2. Os scripts de implantação devem executar collMod na ordem das dependências (primeiro usuários, depois pedidos, pois os pedidos fazem referência a users._id); 3. Numeração de versões: inclua o número da versão e a data das alterações em um comentário no início de cada arquivo de esquema para facilitar o rastreamento; 4. Procedimento de reversão: basta reverter o arquivo de esquema usando o Git e executar novamente o script de implantação para reverter as alterações; 5. Integração de CI/CD: execute automaticamente os scripts de migração de esquema dentro do pipeline de implantação para garantir que o código e o esquema sejam atualizados em sincronia. Esse conjunto de práticas elimina o clássico problema de implantação em que “o código mudou, mas o esquema do banco de dados não”.
Estratégia de implementação para migrações de esquema: As alterações no esquema devem ser implementadas em fases, assim como o código — 1. Fase 1: Torne o novo campo opcional (moderate + warn) e monitore por 1 a 2 semanas para confirmar que não há problemas; 2. Fase 2: Use scripts de migração para preencher dados históricos (bulkWrite + $set) a fim de garantir que todos os documentos contenham os novos campos; 3. Fase 3: Defina os novos campos como obrigatórios (strict + error), garantindo que novos documentos os incluam; 4. Fase 4: O código da aplicação começa a usar os novos campos (anteriormente, era “usar se estiver presente, ignorar se estiver ausente”). Cada fase é implantada de forma independente; se surgir um problema, apenas a fase atual é revertida. Evite implementar todas as alterações de uma só vez — embora “adicionar campos, preencher dados históricos e, em seguida, defini-los como obrigatórios” possa parecer ineficiente, cada etapa é verificável e reversível, tornando-se a única maneira de garantir a segurança da produção.
graph LR
A[Analyzing New Demand] --> B[Design New Rules]
B --> C[validationLevel: moderate<br/>validationAction: warn]
C --> D[Observation Log<br/>Frequency of Violations]
D --> E{Many violations?}
E -->|Many| F[Adjustment Rules/Data Migration]
E -->|Few| G[validationAction: error]
F --> D
G --> H[validationLevel: strict<br/>(Optional)]
style C fill:#fff3cd
style G fill:#d4edda
| Operação | Comando | Observações |
|---|---|---|
| Adicionar validador | collMod + validator |
moderar para evitar afetar dados antigos |
| Regras de modificação | collMod + new validator |
Substituição completa, não modificação incremental |
| Excluir verificador | collMod + validator:{} + level:off |
Para desativação temporária |
| Adicionar um campo obrigatório | Tornar os novos campos opcionais inicialmente e, depois, obrigatórios | Adotar uma abordagem gradual para evitar o bloqueio da entrada de dados |
Melhores práticas para a evolução do esquema: Alterações no esquema em ambientes de produção exigem uma abordagem cautelosa e incremental — 1. Adicionar campos: primeiro, defina-os como opcionais com um valor padrão (para que os dados legados sejam preenchidos automaticamente com o padrão); após um período de operação para confirmar que não há problemas, altere-os para obrigatórios; 2. Aperfeiçoar restrições: primeiro, use validationAction: warn para monitorar; após confirmar que não há violações, mude para error; 3. Remoção de campos: primeiro, interrompa a gravação no campo na camada de aplicação; após confirmar que os dados legados não estão mais sendo lidos, use $unset para limpá-los em massa; 4. Renomeação de campos: primeiro, adicione o novo campo e grave simultaneamente nos campos novo e antigo; após migrar os dados, exclua o campo antigo. É necessário um plano de reversão para cada etapa.
A relação entre $jsonSchema e JSON Schema: O $jsonSchema do MongoDB é baseado na especificação JSON Schema draft-4, mas foi adaptado para incluir suporte a bsonType (que estende tipos BSON como ObjectId e Decimal128), required, properties, pattern, minimum/maximum, minItems/maxItems e outros. Palavras-chave não suportadas: $ref (referências externas não são suportadas), definitions (definições reutilizáveis não são suportadas) e anyOf/oneOf/allOf (validação combinada não é suportada). Essas limitações significam que o $jsonSchema é adequado para “validação estrutural” (tipo de campo + intervalo + formato), mas não para validação lógica complexa entre campos — esta última deve ser implementada na camada do Mongoose.
// === Add a validator to an existing collection ===
db.runCommand({
collMod: 'users',
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['email', 'username'],
properties: {
email: { bsonType: 'string', pattern: '^.+@.+$' }
}
}
},
validationLevel: 'moderate',
validationAction: 'error'
});
// === Modify the Validator ===
db.runCommand({
collMod: 'users',
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['email', 'username', 'age'], // New age Required
properties: {
email: { bsonType: 'string', pattern: '^.+@.+$' },
age: { bsonType: 'int', minimum: 0 }
}
}
}
});
// === Remove Validator ===
db.runCommand({
collMod: 'users',
validator: {},
validationLevel: 'off'
});
Análise dos pontos-chave:
- O validador para
collModrealiza uma substituição completa, e não uma fusão — é necessário escrever a regra completa toda vez que fizer uma alteração. - Ao adicionar um novo campo “obrigatório”, recomenda-se começar com a configuração “moderado + aviso” e mudar para “rigoroso + erro” somente após confirmar que os dados existentes são aceitáveis.
- Exclua o validador usando um objeto vazio
{}; não exclua o campo “validator”.
6. Esquema do Mongoose vs. $jsonSchema do MongoDB
Explicação do conceito: O Mongoose Schema e o $jsonSchema do MongoDB são duas camadas complementares de mecanismos de validação de dados. O Mongoose realiza a validação na camada de aplicação (dentro do processo do Node.js); ele é flexível, mas se aplica apenas a clientes Node.js. O $jsonSchema realiza a validação na camada do banco de dados (dentro do processo do mongod); ele é rigoroso, mas se aplica a todos os clientes.
Análise comparativa:
| Dimensão | Esquema do Mongoose | $jsonSchema do MongoDB |
|---|---|---|
| Camada de execução | Camada de aplicação (Node.js) | Camada de banco de dados (MongoDB) |
| Desempenho | Verificado no processo de inscrição | Verificado no banco de dados |
| Flexibilidade | ✅ Validação assíncrona, funções personalizadas | ❌ Apenas regras estáticas |
| Multilíngue | ❌ Apenas Node.js | ✅ Funciona com qualquer driver |
| Validação complexa | ✅ Qualquer código JS | ❌ Limitado ao JSON Schema |
| Validação aninhada | ✅ Aninhamento profundo + referências | ✅ Propriedades aninhadas |
| Mensagens de erro personalizadas | ✅ Personalizadas por campo | ❌ Mensagens de erro genéricas |
| Modificações em tempo de execução | ✅ Adição/remoção dinâmica | ✅ Modificação online do collMod |
graph LR
A[Client Request] --> B[mongoose Schema<br/>Application-Layer Validation]
B -->|Through| C[MongoDB $jsonSchema<br/>Database-Level Validation]
B -->|Failure| D[❌ Application Layer Rejection<br/>Custom Error Messages]
C -->|Through| E[✅ Write successful]
C -->|Failure| F[❌ Database Rejection<br/>DocumentFailedValidation]
style B fill:#cce5ff
style C fill:#d4edda
style D fill:#f8d7da
style F fill:#f8d7da
Melhores práticas:
- Validação dupla na camada de aplicação (Mongoose) e na camada de banco de dados ($jsonSchema)
- Use o Mongoose para validação dinâmica e detalhada (como “força da senha” e “validação de relações entre campos”)
- Use
$jsonSchemapara a validação básica do esquema (como alternativa) a fim de impedir gravações diretas que contornem a camada de aplicação
▶ Exemplo 2: Mongoose + $jsonSchema – Validação dupla
// ShopHub:Two-Factor Verification for User Registration
// 1. mongoose layer: Flexible Validation + Custom Message
const userSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Email is required'],
match: [/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, 'Invalid email format']
},
username: {
type: String,
required: true,
minlength: [3, 'Username must be at least 3 characters'],
maxlength: 30,
validate: {
validator: async function(v) {
const count = await this.constructor.countDocuments({ username: v });
return count === 0;
},
message: 'Username already exists'
}
},
age: { type: Number, min: 18, max: 120 },
role: { type: String, enum: ['customer', 'admin', 'moderator'], default: 'customer' }
});
// 2. $jsonSchema layer: Infrastructure Safety Net
db.runCommand({
collMod: 'users',
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['email', 'username'],
properties: {
email: { bsonType: 'string', pattern: '^.+@.+$' },
username: { bsonType: 'string', minLength: 3 },
age: { bsonType: 'int', minimum: 18 },
role: { enum: ['customer', 'admin', 'moderator'] }
}
}
},
validationLevel: 'moderate',
validationAction: 'error'
});
// 3. Effect: Node.js client uses double verification, other clients are caught by $jsonSchema safety net
7. Estratégia de evolução do esquema
Explicação do conceito: A evolução do esquema é um desafio operacional fundamental para o banco de dados sem esquema do MongoDB. Embora o MongoDB não exija um esquema predefinido, os dados de produção sempre possuem uma estrutura implícita. Quando os requisitos de negócios mudam, as regras de validação devem ser modificadas com segurança, sem interromper as operações comerciais.
Princípios da Evolução:
- Incremental: opcional no início, obrigatório posteriormente; aviso primeiro, erro depois
- Compatibilidade: As novas regras são compatíveis com os dados existentes e não os invalidam retroativamente.
- Capacidade de reversão: Cada alteração é registrada de acordo com as regras anteriores, permitindo uma reversão rápida, se necessário
- Dados em primeiro lugar: migre os dados primeiro e, depois, torne as regras mais rigorosas
Lista de verificação para a evolução segura do esquema: Diferentes tipos de alterações no esquema apresentam níveis variados de risco — 1. Operações seguras (podem ser executadas diretamente): Adicionar campos opcionais, aumentar maxLength, diminuir minimum, adicionar valores de enumeração, adicionar o atributo $jsonSchema (sem alterar required); 2. É necessário cautela (é preciso realizar primeiro a migração de dados): Adicionar um campo obrigatório, restringir o minLength/mínimo, remover valores de enumeração, alterar o bsonType; 3. Alto risco (é necessária uma avaliação abrangente): Remover campos, alterar a semântica de um campo (por exemplo, alterar “idade” de “idade” para “ano de nascimento”), alterar o tipo de um campo obrigatório. Operações seguras podem ser realizadas diretamente no ambiente de produção; operações que exigem cautela devem primeiro ser validadas no ambiente de teste; operações de alto risco exigem um plano de migração abrangente, uma estratégia de reversão e uma implementação em fases.
Diretrizes de colaboração entre equipes para a evolução do esquema: As alterações no esquema envolvem várias equipes — 1. Equipe de back-end: definir regras do esquema + escrever scripts de migração; 2. Equipe de front-end: adaptar formulários e exibições para acomodar novos campos; 3. Equipe de DBA: executar collMod + monitorar o desempenho do banco de dados; 4. Equipe de QA: verificar a correção dos scripts de migração + desenvolver planos de reversão. Processo de colaboração: 1. A equipe de back-end envia um PR para as alterações no esquema (incluindo scripts de migração e de reversão); 2. A equipe de front-end envia um PR para sincronização e adaptação; 3. A revisão de código confirma o escopo das alterações; 4. Executar a migração e validar no ambiente de teste; 5. Implantar na produção por meio de implantação gradual (começando com erros do tipo “aviso” e, em seguida, erros do tipo “erro”); 6. Monitorar por 1 a 2 semanas para confirmar que não há anomalias. Esse processo padronizado evita problemas de colaboração, como “o esquema foi alterado, mas a equipe de front-end não estava ciente”.
Padrões evolutivos comuns:
| Tipo de evolução | Risco | Estratégia |
|---|---|---|
| Adicionar um novo campo opcional | Baixo | Adicionar uma propriedade diretamente (não obrigatório) |
| Adicionar um novo campo obrigatório | Médio | Primeiro opcional → Migração de dados → Depois obrigatório |
| Alterar tipo de campo | Alto | Campo de gravação dupla → Migração → Alterar → Excluir campo antigo |
| Excluir campo | Médio | Primeiro, remova da lista de “obrigatórios” → Verifique se não há dependências → Exclua a propriedade |
| Restringir intervalo de valores | Médio | Primeiro: moderado + aviso → Confirmar → erro |
(1) Adicionar um novo campo (compatível com versões anteriores)
// ✅ Gradual Evolution:The default field is optional.
db.runCommand({
collMod: 'users',
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['email', 'username'], // The old field is still required
properties: {
email: { bsonType: 'string' },
username: { bsonType: 'string' },
age: { bsonType: 'int' } // Add a Field (not required)
}
}
},
validationLevel: 'moderate'
});
(2) Modificar o tipo de campo (requer migração de dados)
Processo de alteração do tipo de campo:
graph LR
A[1.Add a New Field<br/>bsonType:New Type] --> B[2.Dual Writing<br/>The application writes to both new and existing fields simultaneously]
B --> C[3.Data Migration<br/>Old Field→New Field]
C --> D[4.Switch Query<br/>Read New Field]
D --> E[5.Delete Old Fields<br/>Confirm that there are no dependencies]
style A fill:#cce5ff
style E fill:#d4edda
// ⚠️ Exercise Caution When Changing Field Types
// 1. Add a dual-write field
db.runCommand({
collMod: 'users',
validator: { /* Add a New Field,Keep the old field */ }
});
// 2. Data Migration Script
db.users.find({ ageStr: { $exists: true } }).forEach(doc => {
db.users.updateOne(
{ _id: doc._id },
{ $set: { age: parseInt(doc.ageStr) }, $unset: { ageStr: '' } }
);
});
// 3. Remove the old field validation
8. Treinamento prático abrangente
Visão geral do conceito: A Aplicação Prática Abrangente integra todos os recursos essenciais do $jsonSchema em uma definição completa de conjunto de produtos — incluindo validação de tipos, expressões regulares, enumerações, intervalos, documentos aninhados, validação de elementos de matriz e muito mais — para demonstrar todo o escopo da validação de esquemas em nível de produção.
Lista de verificação para o projeto de esquema em ambiente de produção:
| Item a verificar | Palavra-chave | Está incluído? |
|---|---|---|
| Tipo de documento | bsonType: 'object' | ✅ |
| Campo obrigatório | obrigatório | ✅ |
| Comprimento da string | comprimento_mínimo / comprimento_máximo | ✅ |
| Padrão de expressão regular | padrão | ✅ |
| Intervalo de valores | Mínimo / Máximo | ✅ |
| Valor de enumeração | enum | ✅ |
| Elemento da matriz | itens | ✅ |
| Documentos aninhados | Propriedades aninhadas | ✅ |
| nível de validação | moderado | ✅ |
| ação de validação | erro | ✅ |
// === Create a Product Collection(Includes complete Schema Validation)===
db.createCollection('products', {
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['sku', 'title', 'price', 'category'],
properties: {
sku: {
bsonType: 'string',
pattern: '^[A-Z0-9-]+$',
maxLength: 50
},
title: {
bsonType: 'string',
minLength: 1,
maxLength: 200
},
price: {
bsonType: 'decimal'
},
category: {
enum: ['Electronics', 'Books', 'Clothing', 'Home']
},
stock: {
bsonType: 'int',
minimum: 0
},
tags: {
bsonType: 'array',
items: { bsonType: 'string' }
}
}
}
},
validationLevel: 'moderate',
validationAction: 'error'
});
▶ Exemplo: Guia prático sobre o validador $jsonSchema do MongoDB
// 1. Create a collection with validation rules
db.createCollection('users', {
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['email', 'username', 'age'],
properties: {
email: {
bsonType: 'string',
pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
maxLength: 100
},
username: {
bsonType: 'string',
minLength: 3,
maxLength: 30,
pattern: '^[a-zA-Z0-9_]+$'
},
age: {
bsonType: 'int',
minimum: 18,
maximum: 120
},
role: {
enum: ['customer', 'admin', 'moderator']
}
}
}
},
validationLevel: 'moderate', // Validate only newly inserted records/Update
validationAction: 'error' // Reject Illegal Data
});
// 2. Testing Valid Data → Success
db.users.insertOne({
email: 'alice@example.com',
username: 'alice_2026',
age: 28,
role: 'customer'
});
// { acknowledged: true, insertedId: ObjectId('...') }
// 3. Testing Invalid Data → Rejected
db.users.insertOne({
email: 'invalid-email', // Invalid email address
username: 'ab', // The username is too short
age: 15 // Under 18 years old
});
// Throw an error:Document failed validation
// The error message includes the field name and the reason for the failure.
// 4. Modify the Validator(Add a New Rule)
db.runCommand({
collMod: 'users',
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['email', 'username', 'age', 'phone'],
properties: {
email: { bsonType: 'string', pattern: '^.+@.+$' },
username: { bsonType: 'string', minLength: 3 },
age: { bsonType: 'int', minimum: 18 },
phone: { bsonType: 'string', pattern: '^\+?[0-9]{10,15}$' } // New
}
}
}
});
// 5. Nested Document Validation
db.createCollection('orders', {
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['userId', 'items', 'total'],
properties: {
userId: { bsonType: 'objectId' },
items: {
bsonType: 'array',
minItems: 1,
items: {
bsonType: 'object',
required: ['productId', 'qty', 'price'],
properties: {
productId: { bsonType: 'objectId' },
qty: { bsonType: 'int', minimum: 1 },
price: { bsonType: 'decimal', minimum: 0 }
}
}
},
total: { bsonType: 'decimal', minimum: 0 }
}
}
}
});
// 6. Turn Off the Verifier(For example, when migrating data)
db.runCommand({
collMod: 'users',
validator: {},
validationLevel: 'off'
});
Resultado: os dados válidos são inseridos com sucesso; os dados inválidos são rejeitados, e os campos específicos que apresentaram falha são indicados. nível de validação: moderado. Garante que os dados existentes não sejam afetados.
Estratégia operacional para validação de esquema: A validação de esquema requer suporte operacional em ambientes de produção — 1. Estratégia de implantação: Primeiro, configure validationAction: 'warn' (apenas registrar em log, não rejeitar), monitore por 1 a 2 semanas para confirmar se as validações estão funcionando corretamente e, em seguida, mude para error; 2. Revertimento de emergência: Prepare um comando de revertimento (db.runCommand({collMod: 'users', validator: {}, validationLevel: 'off'}) para desativar rapidamente a validação em caso de falsos positivos; 3. Migração de dados: Desative a validação (validationLevel: 'off') antes de executar os scripts de migração e, em seguida, reative-a após a migração; 4. Monitoramento e alertas: Monitore os eventos “falha na validação” nos logs do MongoDB; falhas frequentes indicam que as regras de validação precisam de ajustes; 5. Controle de versão: Armazene a definição do $jsonSchema no controle de versão (arquivo JSON + script de implantação) e faça o lançamento em sincronia com o código da aplicação.
Caminho seguro para a evolução das regras de validação: As modificações nas regras de validação do esquema se enquadram em três categorias — 1. Flexibilização das regras (segura): Adicionar campos opcionais, aumentar maxLength ou diminuir minimum sem comprometer os dados existentes; 2. Endurecimento das regras (arriscado): adição de campos obrigatórios, redução do intervalo de enumeração ou aumento de minimum — os dados existentes podem não atender às novas regras; 3. Mudanças de tipo (mais perigoso): alteração do bsonType de string para int, etc., o que quase certamente causará falhas de validação. Caminho seguro de evolução: Primeiro, flexibilizar as regras (tornar os novos campos opcionais) → Completar os dados (usar scripts para preencher os novos campos nos documentos existentes) → Em seguida, tornar as regras mais rígidas (definir os novos campos como obrigatórios). Reserve de 1 a 2 semanas entre cada etapa para garantir a consistência dos dados.
❓ Perguntas Frequentes
P: Quais validações o $jsonSchema suporta? R: bsonType / required / properties / pattern / minLength / maxLength / minimum / maximum / enum, etc.
P: O que é melhor, o Mongoose ou o $jsonSchema? R: Use os dois. O Mongoose oferece validação flexível na camada de aplicação, enquanto o $jsonSchema atua como uma rede de segurança na camada do banco de dados.
P: A validação do esquema afeta o desempenho? R: O impacto é mínimo. A validação é realizada na camada do banco de dados, e cada inserção ou atualização é validada. Ela pode ser temporariamente desativada durante os horários de pico.
📖 Resumo
- Validador $jsonSchema: define a estrutura do documento
- validationAction: erro (rejeição) / aviso (advertência)
- nível de validação: rigoroso (todos) / moderado (apenas dados novos; recomendado)
- Esquema do Mongoose vs $jsonSchema: complementares
- Evolução do esquema: adicione recursos gradualmente e modifique os tipos com cuidado
📝 Exercícios
- Questão básica (⭐): Crie uma validação $jsonSchema para a coleção
users(e-mail/nome de usuário/idade). - Questão básica (⭐): Verifica a diferença de comportamento entre
validationAction: warnevalidationAction: error. - Exercício avançado (⭐⭐): Use
collModpara modificar o validador de uma coleção existente (adicionar um novo campo). - Problema avançado (⭐⭐): Implemente a validação dupla usando o Mongoose e o $jsonSchema.
- Questão de desafio (⭐⭐⭐): A definição completa do $jsonSchema para a coleção de produtos (incluindo elementos aninhados, matrizes e Decimal128).