szokodiakos / typegoose

Typegoose - Define Mongoose models using TypeScript classes.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] How to set timestamps for submodels.

moar55 opened this issue · comments

The submodel shouldn't be a new collection in the database...

Right now I do this as an alternative:

 @prop({default: Date.now()})
    createdAt: Date

The submodel shouldn't be a new collection in the database...

for this, you need to give an example, because it shouldnt create a new collection.

class LastStatus
  {
    @prop()
     prop1: string

   @prop()
    prop2:string
  }

class Person extends Typegoose {
 @prop()
  lastStatus: LastStatus

@prop()
 prop3: string

@prop()
prop4: number

}
const PersonModel = new Person().getModelForClass(Person);

say here I want the LastStatus model to have a timestamp, from my understanding If I used
getModelForClass It will create a new collection, which is what I don't want...

To avoid this right now I have something like this:

class LastStatus
  {
    @prop()
     prop1: string

   @prop()
    prop2:string

 @prop({default: Date.now()})
    createdAt: Date
  }

this should work without creating a new collection, a collection is only created when the document is saved, and not as a subdocument of a another document / model

import * as mongoose from "mongoose";
import { prop, Typegoose } from "./typegoose/src/typegoose";

class LastStatus extends Typegoose {
    @prop()
    public prop1: string;

    @prop({ required: true, default: Date.now() })
    public createdAt: Date;
}

class Person extends Typegoose {
    @prop({ required: true })
    public lastStatus: LastStatus;

    @prop()
    public prop3: string;

    @prop()
    public prop4: number;
}

const LastStatusModel = new LastStatus().getModelForClass(LastStatus);
const PersonModel = new Person().getModelForClass(Person);

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

    const doc = new PersonModel({lastStatus: new LastStatusModel({})});
    await doc.save();
    console.log(doc.toObject());

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

Thanks again
Well if getModelForClass can be used without creation of a collection I think it would be better to have this instead of explicitly putting the createdAt property in the class:

const schemaOpts = { schemaOptions: { timestamps: { createdAt: true } } };
const LastStatusModel = new LastStatus().getModelForClass(LastStatus,schemaOpts);

is everything solved now?

@hasezoey I have the same issue.

mongoose: 5.7.4
@hasezoey/typegoose: 6.0.0-32

Base Model:

const schemaOptions: SchemaOptions = {
  timestamps: true,
  toJSON: {
    virtuals: true,
    getters: true,
  },
};

@pre<any>('findOneAndUpdate', function(next) {
    //this._update.updatedAt = new Date(Date.now());
    next();
}) //@pre<T> -> https://github.com/nartc/nest-mean/issues/8
@modelOptions({ schemaOptions })
@ObjectType()
export abstract class BaseModel<T> {
    @prop({ default: () => Date.now()})
    @Field()
    createdAt: Date;

    @prop({ default: () => Date.now()})
    @Field()
    updatedAt: Date;

    @Field()
    id: string;
}

Location Model:

@ObjectType()
@index({ geoJson: 1, locationType: 1 }, { unique: true })
export class Location extends BaseModel<Location> {
 
  ... 

  @arrayProp({ itemsRef: Picture })
  @Field(type=> [Picture], { nullable: true })
  pictures: Ref<Picture>[]

  static get model(): ModelType<Location> {
    return getModelForClass(Location);
  }

  static get modelName(): string {
    return this.model.modelName;
  }

  static createModel(): DocumentType<any> {
    return new this.model();
  }
}

Picture model:

export class Picture extends BaseModel<Picture> {
  @prop({ required: true })
  @Field({ nullable: true })
  key!: string;

  @prop()
  @Field({ nullable: true })
  url?: string;

  @Field(type => User)
  @prop({ref: {name: 'User'}, required: true})
  user!: Ref<User>;

  static get model(): ModelType<Picture> {
    return getModelForClass(Picture);
  }

  static get modelName(): string {
    return this.model.modelName;
  }

  static createModel(): DocumentType<any> {
    return new this.model();
  }
}

When I add 2 pictures to the location's pictures, the two pictures have the same createdAt and updatedAt timestamp.

Here is how I add a picture to a location:

    const dbLocation = await this.locationsService.findById(locationId);
    if(!dbLocation) {
      throw new UserInputError(`Can not find any location with ID#${locationId}`);
    }

    const newPicture = Picture.createModel();
    newPicture.key = pictureKey;
    newPicture.user = user;

    const dbPicture = await this.picturesService.create(newPicture);
    dbLocation.pictures = [dbPicture, ...dbLocation.pictures];

    await this.locationsService.update(locationId, dbLocation)

    return dbLocation;

The weird thing about it is that's the only entity I have this issue with.

The work around I found to have it working is using an anonymous function that returns a Date.now() :

@prop({ default: () => Date.now() })
@Field()
createdAt: Date;

@hasezoey/typegoose: 6.0.0-32

update to @typegoose/typegoose@latest / 6.0.4


what does const newPicture = Picture.createModel(); do?


the two pictures have the same createdAt and updatedAt timestamp.

do you mean that when you just update one of them, both change?


otherwise, this dosnt seem to be an typegoose-created issue @cesar3030

Thanks for your quick response.

I updated to @typegoose/typegoose@latest and this is still happening if I don't use @prop({ default: () => Date.now() }).

const newPicture = Picture.createModel(); does: new getModelForClass(Picture)

the two pictures have the same createdAt and updatedAt timestamp.

I mean: I add two different pictures and the second one has the same timestamp as the first one even if the second one has been added 20 seconds after the second one:

First picture document:

{ "_id" : ObjectId("5dbc7c04c660a70020503d8c"), "createdAt" : ISODate("2019-11-01T18:39:03.297Z"), "updatedAt" : ISODate("2019-11-01T18:39:03.297Z"), "key" : "Location/35fde9f5-d1bc_2019-11-01T18:40:03.669Z_location.jpeg", "user" : ObjectId("5d8b7d95f9da06009bdce9bd"), "__v" : 0 }

Second picture document:

{ "_id" : ObjectId("5dbc7c04c660a70020503d8c"), "createdAt" : ISODate("2019-11-01T18:39:03.297Z"), "updatedAt" : ISODate("2019-11-01T18:39:03.297Z"), "key" : "Location/35fde9f5-d1bc_2019-11-01T18:40:03.669Z_location.jpeg", "user" : ObjectId("5d8b7d95f9da06009bdce9bd"), "__v" : 0 }

Each picture document has a key (Bucket key) which is composed of [uuid]_[timestamp]_[picture name].jpeg these keys are generated when the mobile app is requesting an AmazonS3 upload url to upload a picture to S3 from the mobile app. When the upload is done, the mobile app sends a request to my api to create a picture document with the key of the picture that has just been uploaded to S3 and add this new Picture document to the location's pictures array.
You can clearly tell by the timestamp in the key that it is not a concurrency issue since there is 20 seconds between the two urls are generating and I waited the first picture document to exists in the picture collection before uploading the seconde picture.

is your setup like this below, or functions called again for each picture?

// NodeJS: 12.13.0
// MongoDB: 4.2-bionic (Docker)
import { arrayProp, getModelForClass, modelOptions, prop } from "@typegoose/typegoose"; // @typegoose/typegoose@6.0.4
import * as mongoose from "mongoose"; // mongoose@5.7.6

@modelOptions({ schemaOptions: { timestamps: true } })
class Nested {
    @prop()
    public hello?: string;
}

class Something {
    @arrayProp({ items: Nested })
    public nesty!: Nested[];
}
const SomethingModel = getModelForClass(Something);

(async () => {
    await mongoose.connect(`mongodb://localhost:27017/`, { useNewUrlParser: true, dbName: "verifyMASTER", useCreateIndex: true, useUnifiedTopology: true });

    const some = new SomethingModel();
    some.nesty.push({ hello: "1" });

    setTimeout(async () => {
        some.nesty.push({ hello: "2" });

        await some.save();

        console.log(some);

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

almost, nesty should be an array of refs.

  @arrayProp({ itemsRef: Nested })
  pictures: Ref<Nested>[]