szokodiakos / typegoose

Typegoose - Define Mongoose models using TypeScript classes.
MIT License
1.22k stars 136 forks source link

schemaOptions for embedded schemas #227

Closed YussufElarif closed 5 years ago

YussufElarif commented 5 years ago

What I do normally in mongoose is:

import { Schema, model } from 'mongoose';

const SubCategorySchema = new Schema({
    value: {
        type: String
    }
})

const CategorySchema = new Schema({
    value: {
        type: String,
        required: true
    },
    subCategories: [SubCategorySchema]
});

SubCategorySchema.set('toJSON', {
    virtuals: true,
    versionKey: false,
    transform: (doc, ret, options) =>
    {
        delete ret._id;
        return ret;
    }
})

CategorySchema.set('toJSON', {
    virtuals: true,
    versionKey: false,
    transform: (doc, ret, options) =>
    {
        delete ret._id;
        return ret;
    }
});

export const Category = model('Category', CategorySchema);

When the data comes through express and into my web app. The application prints the id rather than the _id for both CategorySchema and SubCategorySchema which is what I want. However, I can't seem to replicate this on typegoose. I can only manage to do it for Category by doing this:

import { Typegoose, prop, arrayProp } from 'typegoose';

import { ICategory, ISubCategory } from './category.interface';

export class SubCategory implements ISubCategory
{
    readonly id: string;

    @prop({ required: true })
    public value: string;
}

export class Category extends Typegoose implements ICategory
{
    readonly id: string;

    @prop({ required: true })
    public value: string;

    @arrayProp({ items: SubCategory, _id: false })
    public subCategories?: SubCategory[];
}

export const CategoryContext = new Category().getModelForClass(Category, {
    schemaOptions: {
        toJSON: {
            virtuals: true,
            versionKey: false,
            transform: (doc, ret, options) => {
                delete ret._id;
                return ret;
            }
        }
    }
});

Is this feature not implemented or have i missed it out from the docs? What other alternatives are there for this?

Stackoverflow link: https://stackoverflow.com/questions/53696331/schemaoptions-for-embedded-models-typegoose

aoi-umi commented 5 years ago

What I do normally in mongoose is:

import { Schema, model } from 'mongoose';

const SubCategorySchema = new Schema({
    value: {
        type: String
    }
})

const CategorySchema = new Schema({
    value: {
        type: String,
        required: true
    },
    subCategories: [SubCategorySchema]
});

SubCategorySchema.set('toJSON', {
    virtuals: true,
    versionKey: false,
    transform: (doc, ret, options) =>
    {
        delete ret._id;
        return ret;
    }
})

CategorySchema.set('toJSON', {
    virtuals: true,
    versionKey: false,
    transform: (doc, ret, options) =>
    {
        delete ret._id;
        return ret;
    }
});

export const Category = model('Category', CategorySchema);

When the data comes through express and into my web app. The application prints the id rather than the _id for both CategorySchema and SubCategorySchema which is what I want. However, I can't seem to replicate this on typegoose. I can only manage to do it for Category by doing this:

import { Typegoose, prop, arrayProp } from 'typegoose';

import { ICategory, ISubCategory } from './category.interface';

export class SubCategory implements ISubCategory
{
    readonly id: string;

    @prop({ required: true })
    public value: string;
}

export class Category extends Typegoose implements ICategory
{
    readonly id: string;

    @prop({ required: true })
    public value: string;

    @arrayProp({ items: SubCategory, _id: false })
    public subCategories?: SubCategory[];
}

export const CategoryContext = new Category().getModelForClass(Category, {
    schemaOptions: {
        toJSON: {
            virtuals: true,
            versionKey: false,
            transform: (doc, ret, options) => {
                delete ret._id;
                return ret;
            }
        }
    }
});

Is this feature not implemented or have i missed it out from the docs? What other alternatives are there for this?

you can try to use this package

YussufElarif commented 5 years ago

@aoi-umi I've reverted back to mongoose. I'll have a try at that package later on. Thanks :)

penumbra1 commented 5 years ago

I had a similar case. I thought I could call setModelForClass on the child schema and pass schema options to it (according to the docs, it would overwrite the existing model for the child schema). I wonder why this doesn't work.

Drageaux commented 5 years ago

schemaOptions work for the top level doc only. When I queried the whole thing with embedded docs, schemaOptions like _id: false does not apply to the returned children object. This happens EVEN IF I set schemaOptions for the embedded schema

@hasezoey do you mind looking into this? Since, schemaOptions don't apply, I can't fetch virtual props of embedded docs either.


@ObjectType()
export class Boardextends Typegoose {
  @Field(type => [Square])
  @arrayProp({ items: Square, default: DEFAULT_BOARD })
  squares: Square[];
}

export const BoardModel = new Board().getModelForClass(Board, {
  schemaOptions: {
    _id: false,
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
});

@ObjectType()
export class Square extends Typegoose {

  @Field(type => File)
  @prop({ enum: File, required: true })
  file: File;

  @Field(type => Int)
  @prop({ required: true })
  rank: number;

  @instanceMethod // tried putting this as prop, works only for top-level returned doc. If Square is a child of something, it doesn't have "name" prop
  get name(this: InstanceType<Square>): string {
    // it's stored as a number so should return the letter
    return `${File[this.file]}${this.rank}`;
  }
}
export const SquareModel = new Square().getModelForClass(Square, {
  schemaOptions: {
    _id: false,
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
});
hasezoey commented 5 years ago

@Drageaux first some things before i can answer: what is(when from typegoose, the imports would be good to know) @Field, @ObjectType, and what from these two do you want to embed | what is child, what is parent?

otherwise, i dont know what you mean, i have an example below, and it just works:

import * as mongoose from "mongoose";
import { instanceMethod, prop, Typegoose } from "typegoose";

class Dummy extends Typegoose {
    @prop({ required: true, default: "hello" })
    public name!: string;

    @instanceMethod
    public hello(...args: any[]) {
        console.log("Dummy hello");
    }
}

class DummyParent extends Typegoose {
    @prop({ required: true, default: "nothello" })
    public anothername!: string;

    @prop({ required: true })
    public dummy!: Dummy;

    @instanceMethod
    public somehello(...args: any[]) {
        console.log("DummyParent somehello");
    }
}

const DummyModel = new Dummy().getModelForClass(Dummy);
const DummyParentModel = new DummyParent().getModelForClass(DummyParent);

(async () => {
    mongoose.set("debug", true);
    await mongoose.connect(`mongodb://mongodb:27017/`, {
        useNewUrlParser: true,
        useFindAndModify: true,
        useCreateIndex: true,
        dbName: "verify227Drageaux",
        user: "user",
        pass: "passwd",
        authSource: "admin",
        autoIndex: true
    });

    const newdm = new DummyModel();
    newdm.hello();

    const newdpm = new DummyParentModel({dummy: newdm});
    newdpm.somehello();
    newdpm.dummy.hello();

    await mongoose.disconnect();
})();

(output from it:)

Dummy hello
DummyParent somehello
Dummy hello
diephil commented 5 years ago

Anyone found a solution on this ? I'm having the same issue (sub-document with _id that I want to transform to id) :/

hasezoey commented 5 years ago

@diephil would #331 help?

diephil commented 5 years ago

Hello @hasezoey , I finally found a workaround for my issue. In a context of an REST API endpoint (with NestJS), I'm calling a parentSchema.toJSON() before sending back my nested entities to the client. The toJSON method searches deeply and replace all _id properties with id. Thank you for your suggestion

hasezoey commented 5 years ago

schemaOptions for embedded schemas

this should work in 6.0.0 (not tested yet), thanks to @modelOptions

hasezoey commented 5 years ago

this can be closed because it is included in v6.0.0 (to an extend)

@Ben305