MongoDB / Intermediate: aggregation and application contracts / Lesson 10
Schema validation and safe evolution
Use explicit BSON-aware validation while evolving mixed document versions safely. Separate new-write enforcement from inventorying and migrating existing data.
On this page
Lesson goals
- Define required fields, types and ranges with $jsonSchema.
- Plan validation level, action and schema-version transitions.
Flexibility still needs a contract
MongoDB collections can contain differing document shapes by default, but an application still needs predictable field meaning. Collection validation can reject writes that violate required fields, BSON types and supported rules. It complements driver and API validation rather than replacing them. Validation does not provide a general foreign-key constraint across collections, and uniqueness belongs to a suitable index. Decide which fields are mandatory, which may be missing, which may be null and which versions remain readable before designing the validator.
Declare BSON types deliberately
The isolated collection definition requires sku and qty, with sku a string and qty a nonnegative BSON int. A JavaScript client must serialize the intended integer type; a double with the same apparent value does not necessarily satisfy bsonType int. Additional fields remain permitted because the example does not set additionalProperties to false. Creation is illustrative only and targets a named training database. An existing collection requires a separately reviewed modification workflow rather than pretending createCollection retrofits every prior document.
db.getSiblingDB('quay_lab').createCollection('inventoryChecked', {
validator: { $jsonSchema: {
bsonType: 'object', required: ['sku', 'qty'],
properties: {
sku: { bsonType: 'string' },
qty: { bsonType: 'int', minimum: 0 }
}
} },
validationAction: 'error', validationLevel: 'strict'
});
Expected accepted shape includes {sku:"PEN",qty:NumberInt(3)}. Missing qty, text qty or negative qty violates the stated contract.
Enforcement does not prove old data is valid
Adding or changing a validator is not a complete scan-and-repair of existing documents. validationLevel determines how validation applies to updates of existing data; strict and moderate have different transition behavior. validationAction determines rejection versus supported warning behavior. Do not assume a warning-only rollout enforces the final invariant. Inventory old shapes, keep rejected records visible to the migration process and agree on a rollback path. Broadly bypassing validation to finish an import can replace one visible failure with hidden inconsistent data.
Inspect a migration population before converting
This read-only inventory groups documents by schemaVersion and the actual BSON type of qty. Missing fields appear through the aggregation type expression instead of being silently classified as valid integers. The output depends on the training collection and is intentionally unspecified. A version marker is useful only when readers and writers implement its meaning consistently. Stage the migration so old readers remain compatible while new writers produce the accepted new shape, then validate convergence before tightening rules or removing fallback reads.
db.getSiblingDB('quay_lab').inventory.aggregate([
{ $group: { _id: { version: '$schemaVersion', qtyType: { $type: '$qty' } }, count: { $sum: 1 } } },
{ $sort: { '_id.version': 1, '_id.qtyType': 1 } }
]);
Use the type/version inventory as evidence for a bounded migration, not as an automatic conversion instruction.
Use structured validation details carefully
A validation failure commonly uses error code 121 and may include rule details, while duplicate-key failures commonly use 11000. They need different remedies. Error text and diagnostic structure can vary by server and driver version, so applications should rely on supported code and structured fields rather than brittle full-message matching. A schema migration must also update indexes, default interpretation and integrations. Keep version-specific JSON Schema support in view: MongoDBโs $jsonSchema includes BSON-specific extensions and is not a promise of every feature in the latest general JSON Schema standard.
Common errors and fixes
Document failed validation (121)
- Why it happens
- The proposed post-write shape violates the active validator.
- How to fix it
- Inspect required fields, BSON types and rule details before correcting input.
- Prevent it next time
- Test driver serialization against the collection contract.
Old invalid records remain after adding validation
- Why it happens
- Write enforcement was confused with retroactive cleanup.
- How to fix it
- Inventory and migrate old data through a separate bounded process.
- Prevent it next time
- Define old-data acceptance and convergence checks before tightening rules.
A valid-looking integer is rejected
- Why it happens
- The driver serialized a different numeric BSON type.
- How to fix it
- Use the documented exact constructor or intentionally widen accepted types.
- Prevent it next time
- Verify types, not only printed values, in fixtures.
Check your understanding
1. The validator requires bsonType int and minimum zero. Is qty:"3" acceptable, and does adding the validator repair existing text values?
Show worked solution
The string is not acceptable under that validator, and installing the rule does not automatically convert existing text values. Inventory the old population, validate which strings represent legitimate quantities and migrate them with explicit rejection handling. Confirm the driver writes the intended integer representation before enforcing the final state. Do not bypass validation merely to conceal serialization mistakes.
2. Why should schemaVersion changes be deployed with reader compatibility before removing old fields?
Show worked solution
Different application instances can overlap during rollout, and older readers may still depend on the previous shape. Add compatible writes and reads, backfill in bounded batches, inspect mismatch or type inventories, then switch the authoritative representation and retire old fields only when all consumers and rollback needs permit it. A version number alone does not coordinate deployment.
Key takeaways
- Validation protects accepted writes; migration proves historical convergence.
- BSON-aware contracts must agree with driver serialization.