mongodb-js / mongoose-autopopulate

Always populate() certain fields in your mongoose schemas
Apache License 2.0
221 stars 36 forks source link

can't use mongoose-autopopulate on recursively nested schema : Maximum call stack size exceeded #101

Closed liitfr closed 1 year ago

liitfr commented 1 year ago

Hello !

First of all, thanks for mongoose & mongoose-autopopulate. I tried to reduce my example to its simplest form. I'm using Nestjs as backend framework.

Reproduction schema is following:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import {
  Document as MongooseDocument,
  Schema as MongooseSchema,
} from 'mongoose';

interface SectionI {
  template?: SectionTemplateI;
  text: string;
  date: Date;
  subSections?: SectionI[];
}

interface SectionTemplateI {
  section: SectionI;
}

export type SectionDocument = Section & MongooseDocument;

@Schema()
export class Section implements SectionI {
  @Prop({
    type: MongooseSchema.Types.ObjectId,
    ref: 'SectionTemplate',
    autopopulate: false,
    required: false,
  })
  template?: SectionTemplateI;

  @Prop({ type: String, required: true })
  text: string;

  @Prop({ type: Date, required: true })
  date: Date;

  subSections?: SectionI[];
}

export const SectionSchema = SchemaFactory.createForClass(Section);

SectionSchema.add({
  subSections: {
    type: [SectionSchema],
    required: false,
  },
});

export type SectionTemplateDocument = SectionTemplate & MongooseDocument;

@Schema()
export class SectionTemplate implements SectionTemplateI {
  @Prop({ type: SectionSchema, required: true })
  section: SectionI;
}

export const SectionTemplateSchema =
  SchemaFactory.createForClass(SectionTemplate);

As you can see, subSections are nested, not relations. I believe it's the root cause of this issue, but don't know if it's expected behavior, and if so, what is the workaround solution.

autopopulate is enabled globally, by following nestjs instruction :

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

import { AppController } from './app.controller';
import { AppService } from './app.service';

import { SectionTemplate, SectionTemplateSchema } from './app.schema';

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost:27018/tbd', {
      connectionFactory: (connection) => {
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        connection.plugin(require('mongoose-autopopulate'));
        return connection;
      },
    }),
    MongooseModule.forFeature([
      { name: SectionTemplate.name, schema: SectionTemplateSchema },
    ]),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Nest throws following error when starting app :

[Nest] 4530  - 10/17/2022, 6:19:11 PM   ERROR [ExceptionHandler] Maximum call stack size exceeded
RangeError: Maximum call stack size exceeded
    at /home/foo/code/test-mongoose/node_modules/mongoose-autopopulate/index.js:238:27
    at Schema.eachPath (/home/foo/code/test-mongoose/node_modules/mongoose/lib/schema.js:1340:5)
    at eachPathRecursive (/home/foo/code/test-mongoose/node_modules/mongoose-autopopulate/index.js:238:10)
    at /home/foo/code/test-mongoose/node_modules/mongoose-autopopulate/index.js:241:7
    at Schema.eachPath (/home/foo/code/test-mongoose/node_modules/mongoose/lib/schema.js:1340:5)
    at eachPathRecursive (/home/foo/code/test-mongoose/node_modules/mongoose-autopopulate/index.js:238:10)
    at /home/foo/code/test-mongoose/node_modules/mongoose-autopopulate/index.js:241:7
    at Schema.eachPath (/home/foo/code/test-mongoose/node_modules/mongoose/lib/schema.js:1340:5)
    at eachPathRecursive (/home/foo/code/test-mongoose/node_modules/mongoose-autopopulate/index.js:238:10)
    at /home/foo/code/test-mongoose/node_modules/mongoose-autopopulate/index.js:241:7
liitfr commented 1 year ago

I also share this example's dependencies

{
  "dependencies": {
    "@nestjs/apollo": "^10.1.3",
    "@nestjs/common": "^9.0.0",
    "@nestjs/core": "^9.0.0",
    "@nestjs/graphql": "^10.1.3",
    "@nestjs/mongoose": "^9.2.0",
    "@nestjs/platform-express": "^9.0.0",
    "apollo-server-express": "^3.10.3",
    "graphql": "^16.6.0",
    "mongoose": "^6.6.5",
    "mongoose-autopopulate": "^0.16.1",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^7.2.0"
  },
  "devDependencies": {
    "@nestjs/cli": "^9.0.0",
    "@nestjs/schematics": "^9.0.0",
    "@nestjs/testing": "^9.0.0",
    "@types/express": "^4.17.13",
    "@types/jest": "28.1.8",
    "@types/node": "^16.0.0",
    "@types/supertest": "^2.0.11",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0",
    "eslint": "^8.0.1",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
    "jest": "28.1.3",
    "prettier": "^2.3.2",
    "source-map-support": "^0.5.20",
    "supertest": "^6.1.3",
    "ts-jest": "28.0.8",
    "ts-loader": "^9.2.3",
    "ts-node": "^10.0.0",
    "tsconfig-paths": "4.1.0",
    "typescript": "^4.7.4"
  },
}
vkarpov15 commented 1 year ago

Fixed here: https://github.com/mongodb-js/mongoose-autopopulate/pull/106