简体   繁体   中英

mongoose conditional required field validation

According to mongoose built-in validators documentations , I can use conditional required field:

const schema = mongoose.Schema({
    a: {
        type: String,
        required: function () {
            return this.b === 1
        }
    },
    b: {
        type: Number,
        required: true
    }
});

In this schema the property a is required only when property b equals to 1 .

Trying to create a new document works as expected:

Model.create({ b: 1 }); // throws ValidationError (property a is required)

and

Model.create({ b: 2 }); // creates document

My problem is trying to update an existing document and set property b to 1 so property a should be required.

Running the following code:

Model.findByIdAndUpdate(model._id, { b: 1 }, { new: true, runValidators: true});

unexpectedly updates the document without throwing an error that property a is required.

My guess is that the validation is running only for the updated properties (property b ) and not the whole document.

I am not sure if this is the expected behavior or a bug...

Am I missing something? Is there any way to run the validators for the whole document and not only the updated properties without having to manually fetch the document before?

你必须实现通常称为“钩子”的中间件,你可以阅读更多内容并实现你的需求https://mongoosejs.com/docs/middleware.html

After playing around with middlewares and validators without any success, I could achieve this requirement by using transactions (Available from MongoDB 4.0 and Mongoose 5.2.0 )

// Create a transaction session
const session = await mongoose.startSession();
session.startTransaction();

// Fetch the model (pass the session)
const model = await Model.findById(modelId).session(session);

// ... update your model here

// Validate the schema
if (model.b === 1 && !model.a) {
    throw new mongoose.ValidationError('property a is required');
}

// Save the changes
await model.save();
await session.commitTransaction();

Note that I don't attach the session to the save function because it is already attached from fetching the model using find :

If you get a Mongoose document from findOne() or find() using a session, the document will keep a reference to the session and use that session for save().

One problem I found with this approach is that MongoDB currently only supports transactions on replica sets. There is an option to run it locally without replica sets for development using run-rs

To run a local replica set for development on macOS, Linux or Windows, use npm to install run-rs globally and run run-rs --version 4.0.0. Run-rs will download MongoDB 4.0.0 for you.

For further information you can check both Mongoose documentations for transactions and MongoDB documentations

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM