Search code examples
javascriptsqltypescriptsequelize.jstypeorm

In TypeORM how do I have a pre-calculated field on an Entity based on other fields of that Entity?


I want to make a 'rating' field on my User Entity. The User Entity has a relationship to the Rating Entity, on User there is a field called ratingsReceived, which is an eager load of all Ratings assigned to that User.

I want the 'rating' field on User to be a mean calculation of all rating values which is a field on Rating Entity called 'ratingValue'.

So essentially I want this calculation to be the value of every User 'rating' field:

ratingsReceived.reduce((acc, curr) => acc + curr.ratingValue, 0) / ratingsReceived.length

The fields in question are 'ratingsReceived' on User:

  @OneToMany(
    () => Rating,
    rating => rating.ratingTo
  )
  ratingsReceived: Rating[];

And 'ratingValue' on Rating:

  @Column('decimal')
  @Min(0)
  @Max(5)
  ratingValue: number;

Solution

  • After trial and error, managed to create a workaround for this like so:

    // After load is called after the entity loads during find() and similar
    // I placed this decorator on my User Entity
    @AfterLoad()
      calculateRating = async () => {
        const result = await getRepository(Rating)
          .createQueryBuilder('ratings')
          .where('ratings."ratingToId" = :id', { id: this.id })
          .getRawAndEntities();
    
        const ratingsAboveZero = result?.entities?.filter(x => parseFloat(x.ratingValue));
        const count = ratingsAboveZero.length;
    
        if (count > 0) {
          this.rating =
            ratingsAboveZero.reduce((acc, curr) => {
              return acc + parseFloat(curr.ratingValue);
            }, 0) / count;
    
          this.ratingCount = count;
        } else {
          this.rating = 0;
          this.ratingCount = 0;
        }
      };