TypeGoose is transforming all of my subdocuments into ObjectId when creating?

114 views Asked by At

I'm working with TypeGoose right now. When I do

const testData= {
"title": "Test Game 1",
    "scenes": [
    {
        "sceneName": "Northern Milestone",
        "dmNotes": [
            {
                "content": "A crossroads where an path branches off towards a town with hostels. Much of the game's map can be seen with limited detail from the great plateau this crossroads sits atop."
            }
        ],
        "characters": [
            {
                "characterName": "Ian",
                "isPlayerCharacter": true,
            },
            {
                "characterName": "Wanderful"
                
            }
        ]
    },
    {
        "sceneName": "North Woods",
        "characters": [],
        "dmNotes": [
            {
                "content": "A thick woods, perceptive characters may smell faint smoke."
            }
        ]
    }
],
    "items": []
}

    mongoose.set("debug", true);
    await GameModel.create(testData);

I get the database call:

Mongoose: games.insertOne({ title: 'Test Game 1', templateChildren: [], scenes: [ { _id: ObjectId("64efc2a0eb4110d73787c7ff") }, { _id: ObjectId("64efc2a0eb4110d73787c800") } ], items: [], _id: ObjectId("64efc2a0eb4110d73787c7fe"), __v: 0}, {})

Why are the scenes being turned into ObjectId?

Here's my schemas:

export class Character {

    @prop({required: true, default: false})
    isPlayerCharacter!: boolean;

    @prop({required: true, default: 'Unnamed Character'})
    characterName!: string;

    @prop({required: true, default: false})
    template!: boolean;

    @prop({type: () => [DMNote], default: []})
    dmNotes!: DMNote[];

    @prop({required: true, default: new Stats()})
    stats!: Stats;

    @prop({required: true, default: new SkillStats()})
    skills!: SkillStats;

    @prop({type: () => [Item], default: []})
    items!: Item[];

    @prop({required: true, default: 0})
    sceneId!: number;

    @prop({required: true, default: 0})
    skillStatsId!: number;

    @prop({ref: () => User, required: false})
    player?: Ref<User>;
}


export class SceneClass {
    @prop({required: true, default: 'Unnamed Scene'})
    sceneName!: string;

    @prop({required: true, default: false})
    template!: boolean;

    @prop({type: () => [Character], default: []})
    characters!: Character[];

    @prop({type: () => [DMNote], default: []})
    dmNotes!: DMNote[];

    @prop({type: () => [Item], default: []})
    items!: Item[];
}
export class Game {
    @prop()
    title?: string;

    @prop()
    isTemplate!: boolean;

    @prop({ref: () => Game})
    templateChildren?: Ref<Game>[];

    @prop({ref: () => Game})
    templateParent?: Ref<Game>;


    @prop({type: () => [SceneClass], required: true, default: []})
    scenes!: SceneClass[];

    @prop({type: () => [Item]})
    items: Item[] = [];

    @prop({ref: () => User})
    owner?: Ref<User>;

    public getCharacters(this: DocumentType<Game>) {
        return this.scenes.map((scene) => scene.characters || []).flat();
    }

    public findCharacterByName(this: DocumentType<Game>, characterName: string) {
        return this.getCharacters().find((character) => character.characterName === characterName);
    }

    public async getUsersCharacterInThisGame(this: DocumentType<Game>, user: DocumentType<User>) {
        return this.getCharacters().find((character) => character.player?._id.toHexString() === user._id.toHexString());
    }

    public async getSceneWithUser(this: DocumentType<Game>, user: DocumentType<User>) {
        const playedCharacter = await this.getUsersCharacterInThisGame(user);
        if (!playedCharacter) return undefined;
        return this.scenes.find((scene) => scene.characters.find((character) => character.characterName === playedCharacter.characterName))
    }
}

export class User {
    @prop({required: true, default: '', unique: true})
    userName!: String;

    @prop({type: () => [MessageClass], required: true, default: []})
    sentMessages?: MessageClass[];

    @prop({type: () => [MessageClass], required: true, default: []})
    receivedMessages!: MessageClass[];

}

That's not the whole datamodel, but it's what looked immediately relevant. Let me know if you need anything else defined before we discuss.

1

There are 1 answers

0
teos On

When you use type: () => [SceneClass] with Typegoose, it's going to cast the provided value.

And since your class has not constructor, it will not pickup any properties. You will be left with empty object with only _id in them.

You should add a constructor:

export class SceneClass {
    constructor(data: unknown) { // or your own input type instead of "unknown"
        Object.assign(this, data);
    }
// [...]
}

and create new Objects in your testData:

"scenes": [
    new SceneClass({
        "sceneName": "Northern Milestone",
// [...]
]

(not sure you need the second part in your case, I can't try it right now)