mongoose icon indicating copy to clipboard operation
mongoose copied to clipboard

Map of nested discriminators

Open nhitchins opened this issue 6 years ago • 6 comments

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

nhitchins avatar Nov 20 '19 01:11 nhitchins

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 avatar Mar 28 '20 21:03 bbhopesh

@bbhopesh one alternative is to create a separate CarModel and TruckModel, and validate against those. return new CarModel(vehicle.details).validate().

vkarpov15 avatar Apr 02 '20 21:04 vkarpov15

Hmmm.. that is an acceptable workaround for now but I think just schema should be able to take an object and validate.

bbhopesh avatar Apr 04 '20 08:04 bbhopesh

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().

vkarpov15 avatar Apr 10 '20 20:04 vkarpov15

Great Thanks.

bbhopesh avatar Apr 26 '20 19:04 bbhopesh

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!

zburkhardt-zz avatar Apr 21 '22 16:04 zburkhardt-zz