Search code examples
node.jsmongodbmongoose

Mongoose setting strict to false disables validation


I have the following schema

var Schema = new mongoose.Schema({
    type: {required: true, type: String, enum: ["device", "beacon"], index: true},
    device: {
        type: {type: String},
        version: {type: String},
        model: {type: String}
    },
    name: String,
    beaconId: {required: false, type: mongoose.Schema.Types.ObjectId},
    lastMeasuredTimestamp: {type: Number, index: true},
    lastMeasuredPosition: {type: [Number], index: "2dsphere"},
    lastMeasuredFloor: {type: Number, index: true}
}, {strict: false});

Note that I have set strict to false. This is because it is valid to add custom properties not defined in the schema to the document.

Next I do the following query DB.Document.update({_id: "SOME_ID_HERE"}, {$set: {type: "bull"}}, {runValidators: true})

This will change the property 'type' to a value which is invalid according to the Mongoose schema. I use the runValidators option to ensure that the schema validation is run.

The end result of this query however is that 'type' is changed to 'bull' and no validation is run. When I set strict to true however the validation does run and an error is (correctly) shown.

Why does strict influence whether or no the validation runs? When I look at this description http://mongoosejs.com/docs/guide.html#strict it only mentions that strict limits adding properties not defined in the schema (something I do not want for this specific schema).

Install info:

  • Ubuntu 14.04 LTS
  • MongoDB 3.0.8
  • Mongoose 4.2.3
  • NodeJS 0.10.25
  • NPM 1.3.10

Solution

  • After some trying out I figured out a solution that did work. I post it here should any future Mongoose users run into this same problem.

    The trick is to use the save method on a Mongoose document. For some reason this does run the validators properly while also allowing the use of the strict option.

    So the basic process for updating a document would look like this:

    • Find the document using Model.findOne
    • Apply the updates to the documents (by merging the updated values in the existing document for example)
    • Call save on the document.

    In code:

        // Find the document you want to update
        Model.findOne({name: "Me"}, function(error, document) {
            if(document) {
                // Merge the document with the updates values
                merge(document, newValues);
    
                document.save(function(saveError) {
                    // Whatever you want to do after the update
                });
            }
            else {
                // Mongoose error or document not found....
            }
        });
    
        // Merges to objects and writes the result to the destination object.
        function merge(destination, source) {
            for(var key in source) {
                var to = destination[key];
                var from = source[key];
    
                if(typeof(to) == "object" && typeof(from) == "object")
                    deepMerge(to, from);
                else if(destination[key] == undefined && destination.set)
                    destination.set(key, from);
                else
                    destination[key] = from;
            }
        }