3
votes

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
1
I think this question is related to this issue in the mongoose GitHub repository. - leroydev
Probably not the answer you're looking for, but you could accomplish adding custom fields and performing your validation operations by using {strict: true}, {runValidators: true}, and defining a field in your schema of {data: {type: Schema.Types.Mixed}} that wraps all your custom fields. - Derek Soike
@leroydev I think you might very well be right, thanks for the link! - Mathyn
@DerekSoike good point about the Schema.Types.Mixed. I did not know this existed. - Mathyn

1 Answers

7
votes

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;
        }
    }