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 methods being ignored, base methods called after app restart #14522

Open
2 tasks done
Kallb123 opened this issue Apr 14, 2024 · 3 comments
Open
2 tasks done
Labels
help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary

Comments

@Kallb123
Copy link

Kallb123 commented Apr 14, 2024

Prerequisites

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

Mongoose version

8.3.0

Node.js version

20.12.2

MongoDB server version

Cloud Atlas

Typescript version (if applicable)

5.4.3

Description

Methods that have been "overloaded" or replaced by discriminated child models are ignored after the app is restarted.

With a base model and discriminated model, the discriminated one can be inserted into Mongo, then retrieved, then the discriminated methods can be triggered. However, if the app is closed and started again, any existing documents in the database are then retrieved as the base model instead of the discriminated model so the base methods are called instead.

Steps to Reproduce

Base documene

export interface IGameData {
    gameId: Types.UUID,
    gameType: string,
}

export interface IGameDataDocument extends IGameData, Document {
    // Instance methods
    CreateResponse: () => Promise<IGameResponse>;
}

export interface IGameDataModel extends Model<IGameDataDocument> {
    // Static methods
}

export var GameDataSchema = new Schema<IGameDataDocument> ({
    gameId: Schema.Types.UUID,
    gameType: String,
}, {discriminatorKey: 'kind'});

GameDataSchema.methods.CreateResponse = async function(): Promise<IGameResponse> {
    console.log("CreateResponse: Generic game");

    return {  
        gameId: this.gameId,
        gameType: this.gameType,
    }
};

export var GameDataModel = models.GameData || model<IGameDataDocument, IGameDataModel>('GameData', GameDataSchema);

Discriminated documents

export interface ISpecialGameData extends IGameData {
    somethingSpecial: boolean,
}

export interface ISpecialGameDataDocument extends ISpecialGameData, IGameDataDocument {
    // Instance methods
}

export interface ISpecialGameDataModel extends Model<ISpecialGameDataDocument> {
    // Static methods
}

var SpecialGameDataSchema = new Schema<ISpecialGameDataDocument>({
    somethingSpecial: Boolean,
}, {discriminatorKey: 'kind'});

SpecialGameDataSchema.methods.CreateResponse = async function(): Promise<ISpecialGameResponse> {
    console.log("CreateResponse: Special game");

    return {
        gameId: this.gameId,
        gameType: this.gameType,
        somethingSpecial: this.somethingSpecial
    };
};

export var SpecialGameDataModel = models.SpecialGameData || GameDataModel.discriminator<ISpecialGameDataDocument, ISpecialGameDataModel>('SpecialGameData', SpecialGameDataSchema);

Usage

const gameDatas: IGameDataDocument[] = await GameDataModel.find({gameType: "whatever"}).exec();

const gameResponses: IGameResponse[] = await Promise.all(gameDatas.map(async gameData => await gameData.CreateResponse()));
// before restart prints: CreateResponse: Special game 
// after restart prints: CreateResponse: Generic game
  1. Create base schema and model
  2. Create discriminated schema and model
  3. Insert a document based upon a discriminated schema
  4. Check methods output discriminated value
  5. Restart the app
  6. Methods now output the base methods rather than discriminated

Expected Behavior

The discriminated methods should be called even if the document wasn't created in this session.

@Kallb123
Copy link
Author

I've even tried getting a raw object and then re-hydrating the document. Even thought the kind is there, it hydrates a base model rather than the discriminated model.

    const gameData: IGameDataDocument = await GameDataModel.findOne({gameId: params.gameid}).exec();
    console.log("gameData", gameData);
    const gameDataObject = gameData.toObject();
    console.log("gameDataObject", gameDataObject);
    const gameDataHydrated: IGameDataDocument = GameDataModel.hydrate(gameDataObject);
    console.log("gameDataHydrated", gameDataHydrated)
    const gameDataResponse = await gameData.CreateDataResponse(); // prints: CreateDataResponse: Generic game 
    const gameDataResponseHydrated = await gameDataHydrated.CreateDataResponse(); // prints: CreateDataResponse: Generic game 
    console.log("responses", gameDataResponse, gameDataResponseHydrated); // Lost all the special details

@Kallb123
Copy link
Author

I've figured it out. When the app is restarted, the discriminated models are no longer "registered". They would only be registered with Mongoose when they are first accessed.

To fix this, I have created a method that initialises all the discriminators on dbConnect() just by referencing each model. Maybe there's a more generic version or different way I should structure my code?

@vkarpov15
Copy link
Collaborator

So the issue is that your discriminators are defined in a file that you don't import until later? Or how are you actually registering your discriminated models?

The dbConnect() method sounds like a reasonable idea: you need to make sure you actually define all your models and discriminators, ideally when your app starts.

@vkarpov15 vkarpov15 added the help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary label Apr 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary
Projects
None yet
Development

No branches or pull requests

2 participants