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

self-references #35

Open
Laubeee opened this issue Oct 25, 2019 · 10 comments
Open

self-references #35

Laubeee opened this issue Oct 25, 2019 · 10 comments
Labels
Bug Something isn't working

Comments

@Laubeee
Copy link

Laubeee commented Oct 25, 2019

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.

@lchimaru
Copy link
Collaborator

Could you provide a use case for such refernece?

@Laubeee
Copy link
Author

Laubeee commented Oct 26, 2019

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

@lchimaru lchimaru added the Bug Something isn't working label Oct 26, 2019
@tuarrep
Copy link

tuarrep commented Nov 19, 2019

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

@grimmer0125
Copy link

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.

@tuarrep
Copy link

tuarrep commented Dec 19, 2019

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

@grimmer0125
Copy link

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.

@tuarrep
Copy link

tuarrep commented Dec 19, 2019

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

@grimmer0125
Copy link

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(

@tuarrep
Copy link

tuarrep commented Dec 29, 2019

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)
    })
  })
);

@Laubeee
Copy link
Author

Laubeee commented Jan 7, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants