Search code examples
mongodbmongoosecollectionssubdocumenttypegoose

Add a new field to the Typegoose subdocument


How do I include an extra field in the User class subdocument?

class Car {
  @prop()
  public model?: string;
}

class User {
  @prop()
  public name?: string;

  @prop({ required: true })
  public age!: number; 

  @prop({ ref: () => Car })
  public cars?: Ref<Car>[]; 
}

Populated Car Collection:

Car A
{
  "_id": "1"
  "model": "Ferrari"
}

Car B
{
  "_id": "2"
  "model": "Tesla"
}

User collection populated according to the default class:

User
{
  "_id": "1",
  "name": "Jonh Doe",
  "age": 25,
  "cars": [
    { "_id": "1" },
    { "_id": "2" }
  ]
}

I need to include a "status" field in the cars array of the User collection, as shown below:

  • status is to identify whether the car model is active for the user
User
{
  "_id": "1",
  "name": "Jonh Doe",
  "age": 25,
  "cars": [
    { "_id": "1", "status": false },
    { "_id": "2", "status": true }
  ]
}


Solution

  • Hasezoey helped me with the github issue.

    Here's the link and his answer.

    https://github.com/typegoose/typegoose/discussions/726

    You have multiple options:

    make the current reference array a subdocumentarray (see example 1) include the status property on the car itself have a separate class that has a unique compound index on the user-car references and has the properties you want and have such a reference field in the user (either through virtual populate or with a explicit ref array) (see example 2)

    Example 1:

    class Car {
      @prop()
      public model?: string;
    }
    
    class UserCarSub {
      @prop({ ref: () => Car })
      public car: Ref<Car>;
    
      @prop()
      public extra_properties: string;
    }
    
    class User {
      @prop()
      public name?: string;
    
      @prop({ required: true })
      public age!: number; 
    
      @prop()
      public cars?: UserCarSub[];
    }
    

    Example 2:

    // the first example does not use virtual populate
    class Car {
      @prop()
      public model?: string;
    }
    
    class UserCarProperties {
      @prop({ ref: () => Car })
      public car: Ref<Car>;
    
      @prop({ ref: () => User })
      public user: Ref<User>;
    
      @prop()
      public extra_properties: string;
    }
    
    class User {
      @prop()
      public name?: string;
    
      @prop({ required: true })
      public age!: number; 
    
      @prop({ ref: () => Car })
      public cars?: Ref<Car>[]; 
    
      @prop({ ref: () => UserCarProperties })
      public car_properties: Ref<UserCarProperties>[];
    }
    
    // the following uses virtual populate
    class User {
      @prop()
      public name?: string;
    
      @prop({ required: true })
      public age!: number; 
    
      @prop({ ref: () => Car })
      public cars?: Ref<Car>[]; 
    
      @prop({ ref: () => UserCarProperties, foreignField: 'user', localField: '_id' })
      public car_properties: Ref<UserCarProperties>[]; // this property does not exist in the database, but can still populate
    }
    

    Virtual Populate

    Alternatively you could also in Example 2 merge the cars references into the UserCarProperties and use that to find cars, that is fully up to how you want it

    Personally i would suggest Example 2 with virtual populate (merged) if the array is of unknown length or could grow to unknown length