MongoDB path · Documents and schemas · Topic 3 of 12

Schema validation

Not started

MongoDB does not require the documents in a collection to share a shape, but it can enforce one. A validator attached to the collection is checked by the server on every insert and update, so it protects the data from every client at once: each service, each migration script, each person in a shell. It is the closest MongoDB gets to the column types and CHECK constraints of a relational table.

Why it matters: Every service that writes to a collection has its own idea of the schema. A validator on the collection is the one place that applies to all of them, including the script someone runs by hand.

  1. 1Validators live on the collection

    A $jsonSchema document (or query operators) is checked on every insert and update, whichever client sends it.

  2. 2The level decides which documents are checked

    strict (the default) checks every insert and update; moderate skips updates to documents that were already invalid.

  3. 3The action decides what happens

    error rejects the write; warn lets it through and logs the violation. 8.1 adds errorAndLog, which does both.

  4. 4Errors explain themselves

    A rejected write returns code 121 with details of which rule failed and for which field.

1

How it works

The explanation, with examples you can run.

A validator

db.createCollection("customers", {  validator: {    $jsonSchema: {      bsonType: "object",      required: ["email", "plan"],      properties: {        email: { bsonType: "string", pattern: "^.+@.+$" },        plan: { enum: ["free", "pro"] }      }    }  },  validationLevel: "strict",  validationAction: "error"}) db.customers.insertOne({ email: "ana@example.com", plan: "gold" })

The insert fails with code 121, "Document failed validation", and the error's errInfo says exactly why:

{  "operatorName": "properties",  "propertiesNotSatisfied": [{    "propertyName": "plan",    "details": [{      "operatorName": "enum",      "specifiedAs": { "enum": ["free", "pro"] },      "reason": "value was not found in enum",      "consideredValue": "gold"    }]  }]}

(Trimmed from errInfo.details.schemaRulesNotSatisfied.) Log or return those details; "Document failed validation" alone sends people hunting.

By default extra fields are allowed. Add additionalProperties: false to forbid them, but then you must list _id too.

Levels and actions

validationLevel decides which writes are checked:

  • strict (the default) checks every insert and every update.
  • moderate checks inserts and updates to documents that are already valid, and lets updates to existing invalid documents through.
  • constraint (new in MongoDB 9.0) also guarantees that every document already in the collection is valid.

validationAction decides what happens to a failing write: error (the default) rejects it, warn accepts it and writes the violation to the server log, and errorAndLog (new in 8.1) rejects it and logs it.

Rolling out a validator

Adding one to a live collection does not touch existing documents. A careful sequence:

  1. Find violators first: db.customers.find({ $nor: [ { $jsonSchema: schema } ] }), with the same schema object, lists documents that do not match.
  2. Apply the validator with collMod and validationAction: "warn", and watch the log for writers you did not know about.
  3. Fix the data and the writers, then switch to error.
db.runCommand({ collMod: "customers", validator: { $jsonSchema: { /* ... */ } }, validationAction: "warn" })

A validator checks one document at a time. It cannot check that a customerId exists in another collection or that a value is unique (that is a unique index's job).

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.You add a validator with validationLevel strict to a collection holding old documents that do not match. What happens to them?
  2. 2.Why might you start a new validator with validationAction warn?
  3. 3.Does a $jsonSchema validator make MongoDB enforce foreign keys between collections?

0 of 3 right so far. Get all three to finish this topic.