Mongo 3.2 have document validation, can we use the same to define a schema instead of using mongoose to do so.? For example :
Mongoose
userschema = mongoose.Schema({
org: String,
username: String,
fullname: String,
password: String,
email: String
});
MongoDB
db.createCollection(
"example",{
validator:{
$and:[
{ "org":{$type:"string"}},
{ "username":{$type:"string"}},
{ "fullname":{$type:"double"}},
{"password":$type:"string"}},
{"email":{$type:"string"}}
]
},
validationLevel:"strict",
validationAction:"error"
})
What ar ethe difference between these tow and can we provide an optional field using validator as in schema ?
Mongoose is not the only ODM library, there are hapijs/joi, MongoDB schemas, etc. And while Mongoose is good especially in areas of inferring data types, we should choose to use the MongoDB schema validation for schemas validation.
Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node. js. It manages relationships between data, provides schema validation, and is used to translate between objects in code and the representation of those objects in MongoDB. MongoDB is a schema-less NoSQL document database.
MongoDB has a feature called schema validation that allows you to apply constraints on your documents' structure. Schema validation is built around JSON Schema, an open standard for JSON document structure description and validation.
Validation is middleware. Mongoose registers validation as a pre('save') hook on every schema by default. You can disable automatic validation before save by setting the validateBeforeSave option. You can manually run validation using doc. validate(callback) or doc.
I use both because they each have different limitations:
Mongoose validators do not run on all types of update queries, and validators only run on paths with values in the update doc because the validators can't know if, for example, a required field is already defined in the database but not in your client's memory (see issue). This is a major reason to use MongoDB validators [in addition to Mongoose validators].
update validators only run on
$set
and$unset
operations (and$push
and$addToSet
in >= 4.8.0).
So you can have a field with required: true
in your Mongoose schema, but an update
operation will not actually require that field! A MongoDB validator can solve this:
db.runCommand({collMod: "collection", validator: {myfield: {$exists: true}}})
MongoDB for the most part cannot reference other fields during validation. For example, you can't say {field1: {$lte: field2}}
. Mongoose validators can reference other fields.
You can do some very basic types of cross-field referencing though:
{validator: {myfield1: "Value 1", $and: [/* other validators */]}
This comes in handy if you're using Mongoose discriminators (inheritance) and have different requirements for each child type.
MongoDB does not provide "nice" errors in case of validation failure; it simply says something like writeError: {code: 121, errmsg: "Document failed validation}
. Mongoose will typically say something like Path 'foo.bar' failed validation
.
MongoDB is fixing this in v4.6.
Abilities that they share:
Type validation. Mongoose by default attempts to cast values to the type specified in the schema. MongoDB with the $type
attribute will cause a validation failure in case of a type mismatch.
Min and max number values. Mongoose uses min
and max
attributes on the schema. MongoDB uses $lt
, $lte
, $gt
and $gte
.
String enums. Mongoose uses enum: [values]
. MongoDB uses $in: [values]
.
String length validation. Mongoose: minlength: 2, maxlength: 10
. MongoDB, use a regex: {fieldname: {$regex: /.{2,10}/}}
.
Array length validation. Mongoose you have to use a custom validator. MongoDB: {fieldName: {$size: 2}}
.
String RegExp matching. Mongoose you have to use a custom validator.
The first bullet point is a major one. MongoDB does not have transactionsnow has transactions, but it does have powerful (and cheap) atomic updates. You often times can't reliably or safely read -> change -> validate -> write with MongoDB, so using MongoDB native validators is critical in these cases.
Since the last answer, MongoDB 4.0 have been released.
the $jsonSchema
feature now have more options than base mongoose Schema validator. (you can add custom validator in mongoose, though).
the use of allOf
, oneOf
, anyOf
and not
operator permit to do complex matching, similar to Mongoose discriminator.
with the $exec
command, it is possible to compare the value of two field of the same document like so :
db.createCollection("test", {
validator : {
$expr : {$gte: ["$budget", "$spend"]}
}
})
will validate that the value of the field budget
must be greater or equal than the value of spend
.
(example adapted from mongodb documentation)
MongoDB still have the problem of non informative error message. Personnally, I validate my data client side (making request to database if necessary to check for uniqueness). This way, the validation of mongodb have error only if there is concurent modification (someone modified the data between the moment you check and the moment you save). when there is mongodb error, I can simply rerun the client side validation to see what wrong.
I think Mongoose is used to it's fullest when used with a find-modify-save strategy, witch permit to use all the feature. this strategy need the use of versioning or locking to prevent concurrent modification.
when going for atomic update (using mongodb operator, update or findAndModify), with the current state of mongodb validation, I would be tempted to not use mongoose (or only use it for the connection management)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With