ts-mongoose icon indicating copy to clipboard operation
ts-mongoose copied to clipboard

self-references

Open Laubeee opened this issue 4 years ago • 10 comments

Hi How would I model a self-reference, such as

const UserSchema = new mongoose.Schema({
    name: { type: String },
    superior: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
});

Both Type.schema().of(UserSchema) and Type.ref(Type.objectId()).of("User", UserSchema) will complain:

Block-scoped variable 'UserSchema' used before its declaration.

Laubeee avatar Oct 25 '19 13:10 Laubeee

Could you provide a use case for such refernece?

lchimaru avatar Oct 25 '19 23:10 lchimaru

The classic one is a person / employee that has a supervisor which is also a person / employee. In my case I need to reference to a "parent" or predecessor object

Laubeee avatar Oct 26 '19 00:10 Laubeee

I've found a workaround:

In mongoose, you have an add method on models that allows you to concatenate model definitions (https://mongoosejs.com/docs/api/schema.html#schema_Schema-add).

I split my model declaration in two steps. First I add everything not related to current model and next with add I add self references.

const UserSchema = createSchema({
  name: Type.string({ required: true }),
  office: Type.string()
});
UserSchema.add(
  createSchema({
    superior: Type.schema().of(UserSchema),
    accountant: Type.schema().of(UserSchema)
  })
);

It works for me as UserSchema is declared before using it in self references.

Hope it helps

tuarrep avatar Nov 19 '19 10:11 tuarrep

I've found a workaround:

In mongoose, you have an add method on models that allows you to concatenate model definitions (https://mongoosejs.com/docs/api/schema.html#schema_Schema-add).

I split my model declaration in two steps. First I add everything not related to current model and next with add I add self references.

const UserSchema = createSchema({
  name: Type.string({ required: true }),
  office: Type.string()
});
UserSchema.add(
  createSchema({
    superior: Type.schema().of(UserSchema),
    accountant: Type.schema().of(UserSchema)
  })
);

It works for me as UserSchema is declared before using it in self references.

Hope it helps

This above example works for me if I only define like this and do not use it. Once I need to assign/access self-reference property, such as user.superior = and it will throw an exception.

grimmer0125 avatar Dec 19 '19 08:12 grimmer0125

@grimmer0125 What kind of exception? It's fine on my side

tuarrep avatar Dec 19 '19 12:12 tuarrep

code snippet:

  1. definition:
const phaseTaskSchema = createSchema(
  {
      status: Type.number(),
  }
);

phaseTaskSchema.add(
  createSchema({
    seriesRef: Type.ref(Type.objectId()).to("PhaseTask", phaseTaskSchema)
  })
);
const model = typedModel("PhaseTask", phaseTaskSchema);
  1. run:
  const p = new db.PhaseTask();
  p.seriesRef = p._id;
  await p.save();

TypeScript: 3.7.2 run tsc:

yarn run v1.16.0 $ tsc index.ts:120:5 - error TS2339: Property 'seriesRef' does not exist on type 'Document & { status: number; _id: ObjectId; __v: number; } & {}'.

120 p.seriesRef = p._id; ~~~~~~~~~

Found 1 error.

error Command failed with exit code 2.

grimmer0125 avatar Dec 19 '19 13:12 grimmer0125

@grimmer0125 Add seriesRef: Type.ref(Type.objectId()) in your createSchema object

tuarrep avatar Dec 19 '19 14:12 tuarrep

Thanks. @tuarrep

The modified code:

const phaseTaskSchema = createSchema(
  {
      status: Type.number(),
      seriesRef: Type.ref(Type.objectId())
  }
);

phaseTaskSchema.add(
  createSchema({
    seriesRef: Type.ref(Type.objectId()).to("PhaseTask", phaseTaskSchema)
  })
);

This time tsc compliling succeeds. But when I run the testing code, got

    throw new TypeError(`Invalid schema configuration: \`${name}\` is not ` +
          ^
TypeError: Invalid schema configuration: `To` is not a valid type at path `seriesRef.to`. See http://bit.ly/mongoose-schematypes for a list of valid schema types.
    at Schema.interpretAsType (/Users/grimmer/git/v2/server/node_modules/mongoose/lib/schema.js:796:11)
    at Schema.path (/Users/grimmer/git/v2/server/node_modules/mongoose/lib/schema.js:572:27)
    at Schema.add (/Users/grimmer/git/v2/server/node_modules/mongoose/lib/schema.js:437:12)
    at Schema.add (/Users/grimmer/git/v2/server/node_modules/mongoose/lib/schema.js:426:14)
    at new Schema (/Users/grimmer/git/v2/server/node_modules/mongoose/lib/schema.js:117:10)
    at Object.exports.createSchema (/Users/grimmer/git/v2/server/node_modules/ts-mongoose/createSchema.js:5:12)
    at Object.<anonymous> (/Users/grimmer/git/v2/server/service/mongoose/PhaseTask.ts:9:23)

PhaseTask.ts:9:23 is this line let phaseTaskSchema = createSchema(

grimmer0125 avatar Dec 19 '19 14:12 grimmer0125

For reference, here a working snippet from my code base:

export const ProductSchema = createSchema({
  diluent: Type.object({ required: true }).of({
    paintbrush: Type.ref(Type.objectId()),
    spray: Type.ref(Type.objectId())
  })
});

/* WORKAROUND: https://github.com/BetterCallSky/ts-mongoose/issues/35 */
ProductSchema.add(
  createSchema({
    diluent: Type.object({ required: true }).of({
      paintbrush: Type.ref(Type.objectId()).to("Product", ProductSchema),
      spray: Type.ref(Type.objectId()).to("Product", ProductSchema)
    })
  })
);

tuarrep avatar Dec 29 '19 08:12 tuarrep

I've found a workaround:

In mongoose, you have an add method on models that allows you to concatenate model definitions (https://mongoosejs.com/docs/api/schema.html#schema_Schema-add).

I split my model declaration in two steps. First I add everything not related to current model and next with add I add self references.

const UserSchema = createSchema({
  name: Type.string({ required: true }),
  office: Type.string()
});
UserSchema.add(
  createSchema({
    superior: Type.schema().of(UserSchema),
    accountant: Type.schema().of(UserSchema)
  })
);

It works for me as UserSchema is declared before using it in self references.

Hope it helps

I think it would be nice to have this kind of example in the docs - for me that'd be enough to close the issue.

Laubeee avatar Jan 07 '20 10:01 Laubeee