Map of nested discriminators
Do you want to request a feature or report a bug? Feature
What is the current behavior? It would be very useful to allow for a map of nested discriminators. While you could make a case that an array of embedded discriminators is a better schema design, sometimes you have to work with a predetermined data structure.
What is the expected behavior?
const shapeSchema = Schema({ name: String }, { discriminatorKey: 'kind' });
const schema = Schema({ shape: {type: Map, of: shapeSchema} });
// error
schema.path('shape').discriminator('Circle', Schema({ radius: String }));
schema.path('shape').discriminator('Square', Schema({ side: Number }));
const MyModel = mongoose.model('ShapeTest', schema);
let doc = new MyModel({ shape: {a: { kind: 'Circle', radius: 5 }, b: { kind: 'Square', side: 10 }} });
doc.shape.a.radius; // 5
doc.shape.b.side; // 10
What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
Node.js 10.16.3 Mongoose 5.7.12 MongoDB 4.0.3
While this feature isn't available, is there a workaround that you would suggest?
I was considering custom validators but I didn't find an easy way to call existing validators from custom validator. Here is an example of what I am trying to do.
var CarSchema; // Big complex car schema.
var TruckSchema; // Big Complex truck schema.
var customValidate = function(vehicles) {
Object.keys(vehicles).forEach(function(vehicleId) {
var vehicle = vehicles[vehicleId];
if (vehicle['kind'] === 'Car' && vehicle['details']) {
// But something like runAllValidators doesn't exist on schema.
// There is a function called doValidate on SchemaType but that
// looks an internal function not meant for public use.
return CarSchema.runAllValidators(vehicle['details']);
} else if (vehicle['kind'] === 'Truck' && vehicle['details']) {
return TruckSchema.runAllValidators(vehicle['details']);
} else {
return false;
}
});
};
var VehicleSchema = new Schema ({
kind : String,
details : new Schema({}, {strict : false})
});
var VehiclesSchema = new Schema ({
vehicles : {
type : Map,
of : VehicleSchema,
validate : [{validator : customValidate, msg : 'Validation failed.'}];
}
});
Does this look like a reasonable workaround? If yes, how can you call all validators of existing schema inside a custom validator? If no, can you please suggest an alternative?
@bbhopesh one alternative is to create a separate CarModel and TruckModel, and validate against those. return new CarModel(vehicle.details).validate().
Hmmm.. that is an acceptable workaround for now but I think just schema should be able to take an object and validate.
The closest thing we have right now is Model.validate(), which is a static function on models that can validate a POJO. We may consider adding the ability to validate an object against a raw schema in the future, but for now you would have to create a separate model and use validate().
Great Thanks.
I stumbled across this issue as I was trying to accomplish the same thing. After much trial and error I was able to get this to work. Given the original example at the top you should be able to do:
schema.path('shape.$*').discriminator('Circle', Schema({ radius: String })); schema.path('shape.$*').discriminator('Square', Schema({ side: Number }));
to add the discriminators to all potential keys in the Map.
Note: I am using Mongoose 6.3.0 so I am not sure at what point this functionality was introduced. Additionally, I have not come across any documentation regarding this so I am unsure if there are potential issues with this approach or if it is even meant to behave in this manner.
Any official confirmation/clarification would be much appreciated!