MongoDB is a NoSQL database known for its flexibility and scalability. One of its key features is that it is schemaless, meaning that documents within a collection do not need to follow a predefined structure. This allows for more agile development and easier handling of evolving data requirements.
However, MongoDB also provides a feature called schema validation, which allows you to enforce rules on the structure and data types of documents within a collection. This ensures data quality and consistency in a database.
What is Schema Validation? ¶
Schema validation in MongoDB allows you to specify rules that document structures must adhere to. These rules are defined using JSON Schema syntax and are associated with a specific collection. When you attempt to insert or update a document, MongoDB checks it against the defined validation rules. If the document violates the rules, MongoDB can either reject the operation or issue a warning, depending on the configured validation action.
Why Use Schema Validation? ¶
- Data Quality: Ensures that only data conforming to your defined structure and types is stored in the collection.
- Consistency: Maintains uniformity across documents, making it easier to query and process data.
- Error Prevention: Catches data entry errors and inconsistencies early in the development cycle.
- Documentation: Provides a clear definition of the expected data structure for a collection.
Specifying Validation Rules ¶
You can specify validation rules when creating a new collection or adding the rules later. MongoDB uses the $jsonSchema
operator to define these rules.
When creating a collection, you pass an option object with a validator
field that contains the JSON schema.
Here's how to do it in the MongoDB shell (mongosh
):
db.createCollection("users", {
validator: {
$jsonSchema: {
... // JSON schema goes here
}
}
})
In Java this looks very similar. The JSON schema can be parsed with Document.parse
, wrapped in a ValidationOptions
instance and then passed to the createCollection
method:
var jsonSchema = Document.parse(
"""
{
$jsonSchema: {
type: "object",
required: [ "username", "status", "address" ],
additionalProperties: false,
properties: {
_id: { bsonType: "objectId" },
username: { type: "string", minLength: 1, description: "username" },
firstName: { type: ["string", "null"] },
lastName: { type: "string" },
email: { type: "string", pattern: "^.+@.+$"},
birthYear: { bsonType: "int", minimum: 1900, maximum: 2025 },
hobbies: {
type: "array",
items: [ { "type": "string", "enum": ["Reading", "Swimming", "Cycling", "Hiking", "Painting"] } ],
minItems: 1,
uniqueItems: true
},
status: { type: "string", enum: [ "active", "inactive" ] },
address: {
type: "object",
required: [ "city" ],
additionalProperties: false,
properties: {
city: { type: "string", minLength: 1 },
street: { type: "string" },
postalCode: { bsonType: "int" }
}
}
}
}
}""");
var validationOptions = new ValidationOptions();
validationOptions.validator(jsonSchema);
db.createCollection("users",
new CreateCollectionOptions().validationOptions(validationOptions));
In this example, the users
collection is created with the following rules:
- The document must be an object.
- The
username
,status
, andaddress
fields are required. - Additional properties are not allowed (i.e., only the specified fields are allowed).
- Because additional properties are not allowed, we must specify the
_id
field, even though it is automatically generated by MongoDB. - The
username
must be a non-empty string. - The
firstName
field can be either a string or null. - The
lastName
field must be a string. - The
email
field must be a string matching the specified pattern. - The
birthYear
must be an integer between 1900 and 2025. - The
hobbies
field must be an array with at least one item, and each item must be one of the specified values. - The
status
field must be either "active" or "inactive". - The
address
field must be an object with a requiredcity
field and optionalstreet
andpostalCode
fields. - The
address.city
field must be a non-empty string. - The
address.street
field is optional, if set must be a string. - The
address.postalCode
field is optional, if set must be an integer.
JSON Schema ¶
MongoDB supports draft 4 of JSON Schema, including core specification and validation specification.
But there are some differences.
Most notable difference is that MongoDB supports the bsonType
keyword, which allows you to specify the BSON type of
field. This is required because the type
keyword in JSON Schema does not cover all types that MongoDB supports.
For example objectId
used for the _id
field is not a valid JSON Schema type, but it is a valid BSON type,
therefore you must use bsonType
to specify it.
bsonType
supports all BSON types listed on this page: BSON Data Types.
MongoDB's schema implementation omits these keywords from the JSON Schema specification:
$ref
$schema
default
definitions
format
id
- The integer type. You must use the BSON type
int
orlong
with thebsonType
keyword. - Hypermedia and linking properties of JSON Schema, including the use of JSON References and JSON Pointers.
- Unknown keywords.
This page in the MongoDB documentation provides a detailed overview of the supported JSON Schema features: MongoDB JSON Schema.
Validation Level and Action ¶
When defining schema validation, you can also specify the validationLevel
and validationAction
.
validationLevel
Determines which documents the validation rules are applied to.
STRICT
: Apply to all document inserts and updates.MODERATE
: Apply rules to all document inserts and only updates to existing valid documents that match the validation rules. Updates to existing documents in the collection that don't match the rules aren't required to pass validation.
The default is STRICT
.
validationAction
Determines how MongoDB handles documents that violate the validation rules.
ERROR
: Reject the operation if the document does not match the validation rules.WARN
: Allow the operation but log a warning if the document does not match the validation rules.
The default is ERROR
.
You set these options with the ValidationOptions
class when creating or modifying a collection.
MongoDatabase db = mongoClient.getDatabase("validation");
Document jsonSchema = Document.parse("""
{
$jsonSchema: {
bsonType: "object",
required: ["name", "phone"],
properties: {
name: {
bsonType: "string",
description: "must be a string"
},
phone: {
bsonType: "string",
description: "must be a string"
}
}
}
}
""");
ValidationOptions validationOptions = new ValidationOptions().validator(jsonSchema)
.validationLevel(ValidationLevel.MODERATE)
.validationAction(ValidationAction.WARN);
db.createCollection("contacts",
new CreateCollectionOptions().validationOptions(validationOptions));
With ValidationAction set to WARN
, MongoDB will allow inserting and updating invalid documents, but it will log a
warning message in the mongod
log.
MongoCollection<Document> contacts = db.getCollection("contacts");
Document contact = new Document("name", "Alice");
InsertOneResult result = contacts.insertOne(contact);
mongodb-validation | {"t":{"$date":"2025-07-30T05:40:24.434+00:00"},"s":"W", "c":"STORAGE", "id":20294, "ctx":"conn15",
"msg":"Document would fail validation",
"attr":{"namespace":"validation.contacts","document":{"_id":{"$oid":"6889b048075f9e8258533baf"},"name":"Alice"},
"errInfo":{"failingDocumentId":{"$oid":"6889b048075f9e8258533baf"},
"details":{"operatorName":"$jsonSchema",
"schemaRulesNotSatisfied":[{"operatorName":"required","specifiedAs":{"required":["name","phone"]},
"missingProperties":["phone"]}]}}}}
Note that the calling application does not see the warning; it is only logged in the MongoDB server logs. The WARN
action is useful if you want to introduce validation rules to an existing collection. This allows you to see which
applications insert or update invalid documents, and you can fix them before switching to ERROR
action.
If we set the action to ERROR
ValidationOptions validationOptions = new ValidationOptions().validator(jsonSchema)
.validationLevel(ValidationLevel.MODERATE)
.validationAction(ValidationAction.ERROR);
and then run the same insertOne operation again, the application will throw a MongoWriteException
exception:
Exception in thread "main" com.mongodb.MongoWriteException: Write operation error on server localhost:27017.
Write error: WriteError{code=121, message='Document failed validation',
details={"failingDocumentId": {"$oid": "6889b2d4ce412af54968e87d"},
"details": {"operatorName": "$jsonSchema",
"schemaRulesNotSatisfied": [{"operatorName": "required", "specifiedAs": {"required": ["name", "phone"]},
"missingProperties": ["phone"]}]}}}.
In both examples the application set the validation level to MODERATE
, which means that the validation rules are
always applied to all inserts. But MongoDB only validates updates to existing documents that were already valid when
the validation rules were applied. That means with this setting, we can update existing documents that are invalid,
and the update of these documents does not have to pass validation. This setting can be useful when you want to
gradually introduce validation rules to an existing collection without needing to migrate all existing documents.
This also means that your application must be prepared to handle documents that do not match the validation rules.
Update Validation Rules ¶
You can update the validation rules of an existing collection using the collMod
command. This command allows you
to modify the validation rules without having to drop and recreate the collection. You can also change the validation
level and validation action with this command.
This example first inserts a document into the contacts
collection.
MongoCollection<Document> contacts = db.getCollection("contacts");
Document contact = new Document("name", "Alice");
InsertOneResult result = contacts.insertOne(contact);
Next the code adds a validation rule to the collection using the collMod
command. The rule requires that the name
and phone
fields are present in each document, and that both fields are strings. The example also sets the validation level and action.
Here you also see how to build the JSON schema rule programmatically using the Document
class.
Document jsonSchema = new Document("$jsonSchema",
new Document("bsonType", "object").append("required", List.of("name", "phone"))
.append("properties",
new Document("name",
new Document("bsonType", "string").append("description",
"must be a string")).append("phone",
new Document("bsonType", "string").append("description",
"must be a string"))));
Document collModCommand = new Document("collMod", "contacts")
.append("validator", jsonSchema).append("validationLevel", "strict") // "strict"
// or
// "moderate"
.append("validationAction", "error"); // "error" or "warn"
Document collModCommandResult = db.runCommand(collModCommand);
We can now see what happens when we update the existing document, which is not valid, according to the new validation
rules. The update will throw a MongoWriteException
exception because the document does not have a phone
field and
validation level is set to strict
.
Bson updateFilter = Filters.eq("name", "Alice");
Bson updateOperation = Updates.set("name", "Alice Updated");
try {
contacts.updateOne(updateFilter, updateOperation);
}
catch (MongoWriteException e) {
System.out.println("Update failed: " + e);
}
But if we would change the validation level to moderate
, the update would succeed, because this document was
inserted before the validation rule was applied and therefore MongoDB does not validate the document.
Document collModCommandModerate = new Document("collMod", "contacts")
.append("validationLevel", "moderate");
Document collModCommandModerateResult = db.runCommand(collModCommandModerate);
System.out.println("Changed validation level to moderate. Result: "
+ collModCommandModerateResult.toJson());
printCollectionInfo(db, "contacts");
// update existing document with validation level moderate
Bson updateFilterModerate = Filters.eq("name", "Alice");
Bson updateOperationModerate = Updates.set("name", "Alice Updated Moderate");
contacts.updateOne(updateFilterModerate, updateOperationModerate);
To view the validation rules of a collection, you can use the listCollections
command. In the options
object of
the collection information, you will find the validator
, validationLevel
, and validationAction
fields.
Document collectionInfo = db.listCollections().first();
System.out.println("Collection: " + collectionName);
Document options = collectionInfo.get("options", Document.class);
System.out.println("Validation Rules: " + options.get("validator"));
System.out.println("Validation Level: " + options.get("validationLevel"));
System.out.println("Validation Action: " + options.get("validationAction"));
Querying and Modifying Valid and Invalid Documents ¶
If you want to introduce validation rules to existing collections, you might need to migrate existing documents to match the new schema. MongoDB makes it easy to query for documents that are valid or invalid.
You can directly pass the JSON schema to the find
method to retrieve documents that match the schema. This also works
with the aggregate
method, where you have to use the $match
stage.
// list all valid documents in the collection
for (Document document : contacts.find(jsonSchema)) {
System.out.println("Valid document: " + document.toJson());
}
// list all valid documents in the collection with aggregation
for (Document document : contacts.aggregate(List.of(Aggregates.match(jsonSchema)))) {
System.out.println("Aggregated valid document: " + document.toJson());
}
To list all invalid documents, you use the $nor
operator with the JSON schema. This operation returns documents that
do not match the specified schema.
for (Document document : contacts.find(Filters.nor(jsonSchema))) {
System.out.println("Invalid document: " + document.toJson());
}
You can use the $nor
operator in update operations to modify or delete invalid documents. This example shows how to
update all invalid documents to make them valid.
Bson updateInvalidFilter = Filters.nor(jsonSchema);
Bson updateInvalidOperation = Updates.set("phone", "000000000");
contacts.updateMany(updateInvalidFilter, updateInvalidOperation);
Here is an example of how to delete all invalid documents.
contacts.deleteMany(Filters.nor(jsonSchema));
Conclusion ¶
This concludes this introduction to MongoDB schema validation and how to use it in Java applications. Schema validation is a valuable tool for ensuring data integrity and consistency. By defining and enforcing rules on your collections, you can improve the reliability and maintainability of your applications.