Skip to content

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

Closed
@ZachLeviPixel

Description

@ZachLeviPixel

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugWe've confirmed this is a bug in Mongoose and will fix it.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions