I wrote a subsriber that listen for review
changes and updates the average rating in the target column.
import { EntityTargetType } from '@src/core/enums/entity-target-type.enum';
import { CourseEntity } from '@src/modules/course/entities/course.entity';
import { HomeworkEntity } from '@src/modules/homework/entities/homework.entity';
import { LectureEntity } from '@src/modules/lecture/entities/lecture.entity';
import { ReviewEntity } from '@src/modules/review/entities/review.entity';
import { EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
@EventSubscriber()
export class ReviewSubscriber implements EntitySubscriberInterface<ReviewEntity> {
constructor() {}
listenTo() {
return ReviewEntity;
}
async afterUpdate(event: UpdateEvent<ReviewEntity>) {
await this.updateTarget(event);
}
async afterRemove(event: RemoveEvent<ReviewEntity>) {
await this.updateTarget(event);
}
async afterInsert(event: InsertEvent<ReviewEntity>) {
await this.updateTarget(event);
}
async updateTarget(event: UpdateEvent<ReviewEntity> | RemoveEvent<ReviewEntity> | InsertEvent<ReviewEntity>) {
const queryRunner = event.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
console.log(event.entity);
try {
let lecture: LectureEntity | null = null;
const query = queryRunner.manager.createQueryBuilder(ReviewEntity, 'review');
query.select('COALESCE(ROUND(AVG(review.rating), 2), 0)', 'rating');
query.addSelect('COALESCE(COUNT(review.id), 0)', 'count');
if (event.entity.targetType === EntityTargetType.LECTURE_BOOK) {
query.leftJoin('lecture_book_entity', 'lectureBook', 'lectureBook.id = review.targetId');
query.leftJoin('lectureBook.lecture', 'lecture');
lecture = await queryRunner.manager.findOne(LectureEntity, {
where: { bookings: { id: event.entity.targetId } },
});
if (!lecture) {
throw new Error(`Lecture not found for booking id ${event.entity.targetId}`);
}
query.where('lecture.id = :lectureId', { lectureId: lecture.id });
} else {
query.where('review.targetId = :targetId', { targetId: event.entity.targetId });
}
// Perform a select query to get the latest review information
const res = await query.getRawOne();
const ratings = {
ratingsCount: res['count'],
ratingsAvg: res['rating'],
};
console.log(ratings, lecture?.id);
if (event.entity.targetType === EntityTargetType.COURSE) {
await queryRunner.manager.update(CourseEntity, { id: event.entity.targetId }, ratings);
} else if (event.entity.targetType === EntityTargetType.HOMEWORK) {
await queryRunner.manager.update(HomeworkEntity, { id: event.entity.targetId }, ratings);
} else if (event.entity.targetType === EntityTargetType.LECTURE_BOOK) {
if (lecture) {
await queryRunner.manager.update(LectureEntity, { id: lecture.id }, ratings);
}
}
await queryRunner.commitTransaction();
} catch (err) {
await queryRunner.rollbackTransaction();
console.error('Error updating target:', err);
} finally {
await queryRunner.release();
}
}
}
The problem is that when I update review from 2
to 3
, the res
stores the old rating not the new one so I guess rating
table in query
is somehow not up to date. Byut why and how to fix that?