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

Discriminated schemas nested with discriminated schemas don't instantiate the correct types #13898

Closed
2 tasks done
ZachLeviPixel opened this issue Sep 26, 2023 · 0 comments · Fixed by #13958
Closed
2 tasks done
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@ZachLeviPixel
Copy link
Contributor

ZachLeviPixel commented Sep 26, 2023

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

7.5.3

Node.js version

18.16

MongoDB server version

6.0.5

Typescript version (if applicable)

No response

Description

When defining a discriminated schema A that has a field which is discriminated schema B, after applying the discriminators at the schema level and instantiating an object of schema A, the types of B within it will always be of the default base type.

If you apply the discriminators for schema B directly on the path, it works as expected. This issue also happens if schema A has a subtype that self-references A. I am not sure how easy it is to convery through text, but I hope the code example makes it clear!

Steps to Reproduce

'use strict';

const mongoose = require('mongoose');
const { Schema } = mongoose;

const baseNestedDiscriminated = new Schema({
    type: { type: Number, required: true },
}, { discriminatorKey: "type" });

class BaseClass {
    type = 1;

    whoAmI() {
        return "I am baseNestedDiscriminated";
    }
}

baseNestedDiscriminated.loadClass(BaseClass);

class NumberTyped extends BaseClass {
    type = 3;

    whoAmI() {
        return "I am NumberTyped";
    }
}

class StringTyped extends BaseClass {
    type = 4;

    whoAmI() {
        return "I am StringTyped";
    }
}

baseNestedDiscriminated.discriminator(1, new Schema({}).loadClass(NumberTyped));
baseNestedDiscriminated.discriminator("3", new Schema({}).loadClass(StringTyped));

const outerDiscriminatedSchema = new Schema({
    type: { type: Number, required: true },
}, { discriminatorKey: "type" });

const containsNestedSchema = new Schema({
    nestedDiscriminatedTypes: { type: [baseNestedDiscriminated], required: true },
})

// If you uncomment these, the example works as expected
//containsNestedSchema.path("nestedDiscriminatedTypes").discriminator(1, new Schema({}).loadClass(NumberTyped));
//containsNestedSchema.path("nestedDiscriminatedTypes").discriminator("3", new Schema({}).loadClass(StringTyped));

class ContainsNested {
    nestedDiscriminatedTypes = [];
    type = 1;

    whoAmI() {
        return "I am ContainsNested";
    }
}
containsNestedSchema.loadClass(ContainsNested);

outerDiscriminatedSchema.discriminator(1, containsNestedSchema);
const secondModel = mongoose.model("containsNestedSchema", containsNestedSchema);
const containerModel = mongoose.model("container", new Schema({items: [baseNestedDiscriminated]}));

void async function main() 
{
    await mongoose.connect("mongodb://127.0.0.1:27017/example");
    mongoose.connection.db.dropDatabase();

    let instance = await containerModel.create({ items: [{type: 1}] }); // "I am NumberTyped" - Works
    console.log(instance.items[0].whoAmI());

    instance = await containerModel.create({ items: [{type: "3"}] }); // "I am StringTyped" Works
    console.log(instance.items[0].whoAmI());

    instance = await secondModel.create({ type: 1, nestedDiscriminatedTypes: [{type: 1}, {type: "3"}] });
    
    console.log(instance.whoAmI()); // "I am ContainsNested" - Works
    instance.nestedDiscriminatedTypes.forEach(item => console.log(item.whoAmI()));
    // Expected:
    // I am NumberTyped
    // I am StringTyped
    // Actual:
    // I am baseNestedDiscriminated
    // I am baseNestedDiscriminated

    mongoose.disconnect();
}();

Expected Behavior

Shown in the code example, once discriminators are applied to a schema, the discriminators should always run wherever the schema is.

@IslandRhythms IslandRhythms added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue 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 Sep 27, 2023
@vkarpov15 vkarpov15 modified the milestones: 7.5.4, 7.5.5 Oct 1, 2023
vkarpov15 added a commit that referenced this issue Oct 9, 2023
vkarpov15 added a commit that referenced this issue Oct 10, 2023
fix(schema): handle embedded discriminators defined using `Schema.prototype.discriminator()`
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
3 participants