Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Model.validate() respect discriminators when discriminator key is set #12621

Closed
1 task done
barca-reddit opened this issue Nov 1, 2022 · 6 comments · Fixed by #12824
Closed
1 task done

Make Model.validate() respect discriminators when discriminator key is set #12621

barca-reddit opened this issue Nov 1, 2022 · 6 comments · Fixed by #12824
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@barca-reddit
Copy link

Prerequisites

  • I have written a descriptive issue title

Mongoose version

5.12.8

Node.js version

v16.18.0

MongoDB version

5.0.13

Operating system

Linux

Operating system version (i.e. 20.04, 11.3, 10)

22.04

Issue

Hello. I have a simple question, and please don't consider this a feature request or something similar, I am merely asking for help. I've searched the docs and online, but I couldn't find an example of this, so I thought I'd ask here.

Is it possible to have discriminators while only using a single model. I think this is best illustrated with an example. Let's say we have 3 schemas, AnimalSchema - the main schema, and two types of animals CatSchema and DogSchema which all have distinct properties, and the discriminator key in AnimalSchema is kind. Finally, we have an Animal model.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const CatSchema = new Schema(
    { meows: { type: Boolean, required: true } }
);

const DogSchema = new Schema(
    { barks: { type: Boolean, required: true } }
);

const AnimalSchema = new Schema(
    {
        _id: { type: Schema.Types.ObjectId, auto: true },
        id: { type: String, required: true },
    },
    {
        discriminatorKey: 'kind',
    }
);

AnimalSchema.discriminator('cat', CatSchema);
AnimalSchema.discriminator('dog', DogSchema);

const Animal = mongoose.model('animal', AnimalSchema);

What I want to do is to instantiate new Animals with their kind, and be able to check their validity like this:

(async () => {
    const validPet = new Animal({
        id: '123',
        kind: 'cat',
        meows: true
    });

    const invalidPet1 = new Animal({
        id: '123',
        kind: 'dog',
        meows: true // <= a dog can't meow
    });

    const invalidPet2 = new Animal({
        id: '123',
        kind: 'INVALID',
        meows: 'INVALID'
    });

    await Animal.validate(validPet); // ✅ valid
    await Animal.validate(invalidPet1); // ❌ invalid
    await Animal.validate(invalidPet2); // ❌ invalid
})();

I am currently using v5.12.8 but the code example provided in this post was made with v6.latest as I thought that might work and I noticed some new syntax and features, but sadly mongoose didn't pick up any errors with invalidPet1 and invalidPet2 when I tried to .validate() against the Animals model.

So yep, don't want to make this any longer than necessary, just wanted to ask if it's possible to do this on v5, v6 or at all. I have done all of this a few years ago with nested paths, but it's not an option in this particular use case.

Thanks.

@barca-reddit barca-reddit added help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted labels Nov 1, 2022
@github-actions
Copy link

This issue is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 5 days

@github-actions github-actions bot added the Stale label Nov 16, 2022
@IslandRhythms
Copy link
Collaborator

Short answer is yes.

Discriminators are a schema inheritance mechanism. They enable you to have multiple models with overlapping schemas on top of the same underlying MongoDB collection.

https://mongoosejs.com/docs/discriminators.html#discriminators-save-to-the-event-models-collection

@github-actions github-actions bot removed the Stale label Nov 17, 2022
@barca-reddit
Copy link
Author

Hey, @IslandRhythms I appreciate the reply but I am afraid that doesn't really help me much. May I ask you for a minimal example of how this can be achieved? As I mentioned I looked for days online and in the docs but I couldn't find anything, perhaps I am missing the obvious?

Discriminators are a schema inheritance mechanism. They enable you to have multiple models with overlapping schemas on top of the same underlying MongoDB collection.

What I am asking is if you can use just a single model with discriminators. Going with the demo I made in the post above, basically I only ever want to have a single AnimalModel (so no cat or dog models) which I can use to create new documents (animals) of kind cat and dog. So if you instantiate a new Animal({ kind: 'dog', meows: 'true' }), it would fail validation because barks is a required prop for a dog.

Cheers!

@IslandRhythms
Copy link
Collaborator

Then no. Its creating a new model for each discriminator.

@barca-reddit
Copy link
Author

Then no. Its creating a new model for each discriminator.

I understand, thanks for clarifying.

Just in case you guys decide to add this as a feature in the future, my motivation for this was (re)-exports and code structure. Because suppose you have 20 different animals, and you don't know ahead of time what animal a 3rd party API might return. You would have to export all 20 models, then import them somewhere else, so it would've been that much easier to just use a single model.

But this is non-crucial for me anyway. Cheers again.

@vkarpov15
Copy link
Collaborator

@barca-reddit the following seems to work fine:

(async () => {
    const validPet = new Animal({
        id: '123',
        kind: 'cat',
        meows: true
    });

    const invalidPet1 = new Animal({
        id: '123',
        kind: 'dog',
        meows: true // <= a dog can't meow
    });

    const invalidPet2 = new Animal({
        id: '123',
        kind: 'INVALID',
        meows: 'INVALID'
    });

    await validPet.validate(); // ✅ valid
    await invalidPet1.validate(); // ❌ invalid
    await invalidPet2.validate(); // ❌ invalid
})();

This just seems like it might be an issue with Model.validate(). Mongoose does correctly infer the animal's discriminator model based on kind, so new Animal({ ...obj, kind: 'dog' }) should be equivalent to new Dog({ ...obj })

@vkarpov15 vkarpov15 reopened this Nov 28, 2022
@vkarpov15 vkarpov15 added this to the 6.7.6 milestone Nov 28, 2022
@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue and removed help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted labels Nov 28, 2022
@vkarpov15 vkarpov15 changed the title Using discriminators with a single model. Make Model.validate() respect discriminators when discriminator key is set Dec 5, 2022
@vkarpov15 vkarpov15 added confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Dec 5, 2022
vkarpov15 added a commit that referenced this issue Dec 26, 2022
fix(model): respect discriminators with `Model.validate()`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants