diff --git a/server/src/entities/album.entity.ts b/server/src/entities/album.entity.ts index 0e55279e9..d8a71d712 100644 --- a/server/src/entities/album.entity.ts +++ b/server/src/entities/album.entity.ts @@ -12,6 +12,9 @@ import { OneToMany, PrimaryColumn, PrimaryGeneratedColumn, + Tree, + TreeChildren, + TreeParent, UpdateDateColumn, } from 'typeorm'; @@ -22,6 +25,7 @@ export enum AssetOrder { } @Entity('albums') +@Tree('closure-table') export class AlbumEntity { @PrimaryGeneratedColumn('uuid') id!: string; @@ -69,6 +73,12 @@ export class AlbumEntity { @Column({ type: 'varchar', default: AssetOrder.DESC }) order!: AssetOrder; + + @TreeChildren() + children!: AlbumEntity[]; + + @TreeParent() + parents!: AlbumEntity[]; } @Entity('nested_albums') diff --git a/server/src/migrations/1712173905900-AddClosureAlbumTable.ts b/server/src/migrations/1712173905900-AddClosureAlbumTable.ts new file mode 100644 index 000000000..08f963b10 --- /dev/null +++ b/server/src/migrations/1712173905900-AddClosureAlbumTable.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddClosureAlbumTable1712173905900 implements MigrationInterface { + name = 'AddClosureAlbumTable1712173905900'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "albums_closure" ("id_ancestor" uuid NOT NULL, "id_descendant" uuid NOT NULL, CONSTRAINT "PK_c73b38b33bc7f8a4b2588c573f5" PRIMARY KEY ("id_ancestor", "id_descendant"))`, + ); + await queryRunner.query(`CREATE INDEX "IDX_a54149056a7a5da2c44d8a65c2" ON "albums_closure" ("id_ancestor") `); + await queryRunner.query(`CREATE INDEX "IDX_3a2f01ca9d654f90f4a2887a36" ON "albums_closure" ("id_descendant") `); + await queryRunner.query(`ALTER TABLE "albums" ADD "parentsId" uuid`); + await queryRunner.query( + `ALTER TABLE "albums" ADD CONSTRAINT "FK_c619d16fe935e8afd5f9105f31f" FOREIGN KEY ("parentsId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "albums_closure" ADD CONSTRAINT "FK_a54149056a7a5da2c44d8a65c22" FOREIGN KEY ("id_ancestor") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "albums_closure" ADD CONSTRAINT "FK_3a2f01ca9d654f90f4a2887a362" FOREIGN KEY ("id_descendant") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "albums_closure" DROP CONSTRAINT "FK_3a2f01ca9d654f90f4a2887a362"`); + await queryRunner.query(`ALTER TABLE "albums_closure" DROP CONSTRAINT "FK_a54149056a7a5da2c44d8a65c22"`); + await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_c619d16fe935e8afd5f9105f31f"`); + await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "parentsId"`); + await queryRunner.query(`DROP INDEX "public"."IDX_3a2f01ca9d654f90f4a2887a36"`); + await queryRunner.query(`DROP INDEX "public"."IDX_a54149056a7a5da2c44d8a65c2"`); + await queryRunner.query(`DROP TABLE "albums_closure"`); + } +} diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index 96cadfd6c..a07864377 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -14,7 +14,16 @@ import { } from 'src/interfaces/album.interface'; import { Instrumentation } from 'src/utils/instrumentation'; import { setUnion } from 'src/utils/set'; -import { DataSource, FindOptionsOrder, FindOptionsRelations, In, IsNull, Not, Repository } from 'typeorm'; +import { + DataSource, + FindOptionsOrder, + FindOptionsRelations, + In, + IsNull, + Not, + Repository, + TreeRepository, +} from 'typeorm'; @Instrumentation() @Injectable() @@ -23,6 +32,7 @@ export class AlbumRepository implements IAlbumRepository { @InjectRepository(AssetEntity) private assetRepository: Repository, @InjectRepository(AlbumEntity) private repository: Repository, @InjectRepository(NestedAlbumEntity) private nestedAlbumRepository: Repository, + @InjectRepository(AlbumEntity) private albumTreeRepository: TreeRepository, @InjectDataSource() private dataSource: DataSource, ) {} @@ -363,6 +373,13 @@ export class AlbumRepository implements IAlbumRepository { @GenerateSql() async getNestedAlbums(id: string): Promise { + const album = await this.repository.findOneOrFail({ where: { id } }); + const ancestor = await this.albumTreeRepository.findAncestorsTree(album); + console.log('Parents', ancestor); + console.log('-----------------'); + const desc = await this.albumTreeRepository.findDescendantsTree(album); + console.log('Children', desc); + const children = await this.repository .createQueryBuilder('albums') .innerJoin('nested_albums', 'nested', 'nested.childId = albums.id')