From cf88f4b6f877a7026e9e7453b473e6580ce6c5ff Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 5 Jul 2024 14:58:34 -0400 Subject: [PATCH] refactor(server): bulk interface (#10889) --- server/src/interfaces/album.interface.ts | 2 -- server/src/interfaces/memory.interface.ts | 6 ++-- server/src/queries/album.repository.sql | 8 ----- server/src/repositories/album.repository.ts | 37 +++++++++----------- server/src/repositories/memory.repository.ts | 4 +-- 5 files changed, 21 insertions(+), 36 deletions(-) diff --git a/server/src/interfaces/album.interface.ts b/server/src/interfaces/album.interface.ts index 48c728febc..2d48f4da7f 100644 --- a/server/src/interfaces/album.interface.ts +++ b/server/src/interfaces/album.interface.ts @@ -28,10 +28,8 @@ export interface IAlbumRepository extends IBulkAsset { getById(id: string, options: AlbumInfoOptions): Promise; getByIds(ids: string[]): Promise; getByAssetId(ownerId: string, assetId: string): Promise; - getAssetIds(albumId: string, assetIds?: string[]): Promise>; hasAsset(asset: AlbumAsset): Promise; removeAsset(assetId: string): Promise; - removeAssetIds(albumId: string, assetIds: string[]): Promise; getMetadataForIds(ids: string[]): Promise; getInvalidThumbnail(): Promise; getOwned(ownerId: string): Promise; diff --git a/server/src/interfaces/memory.interface.ts b/server/src/interfaces/memory.interface.ts index 505e1662cb..308943d97e 100644 --- a/server/src/interfaces/memory.interface.ts +++ b/server/src/interfaces/memory.interface.ts @@ -1,14 +1,12 @@ import { MemoryEntity } from 'src/entities/memory.entity'; +import { IBulkAsset } from 'src/utils/asset.util'; export const IMemoryRepository = 'IMemoryRepository'; -export interface IMemoryRepository { +export interface IMemoryRepository extends IBulkAsset { search(ownerId: string): Promise; get(id: string): Promise; create(memory: Partial): Promise; update(memory: Partial): Promise; delete(id: string): Promise; - getAssetIds(id: string, assetIds: string[]): Promise>; - addAssetIds(id: string, assetIds: string[]): Promise; - removeAssetIds(id: string, assetIds: string[]): Promise; } diff --git a/server/src/queries/album.repository.sql b/server/src/queries/album.repository.sql index 8ac7ed6731..44097a5a8c 100644 --- a/server/src/queries/album.repository.sql +++ b/server/src/queries/album.repository.sql @@ -585,14 +585,6 @@ WHERE "albums_assets"."albumsId" = $1 AND "albums_assets"."assetsId" IN ($2) --- AlbumRepository.getAssetIds (no assets) -SELECT - "albums_assets"."assetsId" AS "assetId" -FROM - "albums_assets_assets" "albums_assets" -WHERE - "albums_assets"."albumsId" = $1 - -- AlbumRepository.hasAsset SELECT 1 AS "row_exists" diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index e3183c36aa..845694f389 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -1,12 +1,10 @@ import { Injectable } from '@nestjs/common'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; -import _ from 'lodash'; -import { Chunked, ChunkedArray, DATABASE_PARAMETER_CHUNK_SIZE, DummyValue, GenerateSql } from 'src/decorators'; +import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { AlbumEntity } from 'src/entities/album.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { AlbumAsset, AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } 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'; const withoutDeletedUsers = (album: T) => { @@ -215,6 +213,10 @@ export class AlbumRepository implements IAlbumRepository { @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] }) @Chunked({ paramIndex: 1 }) async removeAssetIds(albumId: string, assetIds: string[]): Promise { + if (assetIds.length === 0) { + return; + } + await this.dataSource .createQueryBuilder() .delete() @@ -233,27 +235,22 @@ export class AlbumRepository implements IAlbumRepository { * @param assetIds Optional list of asset IDs to filter on. * @returns Set of Asset IDs for the given album ID. */ - @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] }, { name: 'no assets', params: [DummyValue.UUID] }) - async getAssetIds(albumId: string, assetIds?: string[]): Promise> { - const query = this.dataSource + @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] }) + @ChunkedSet({ paramIndex: 1 }) + async getAssetIds(albumId: string, assetIds: string[]): Promise> { + if (assetIds.length === 0) { + return new Set(); + } + + const results = await this.dataSource .createQueryBuilder() .select('albums_assets.assetsId', 'assetId') .from('albums_assets_assets', 'albums_assets') - .where('"albums_assets"."albumsId" = :albumId', { albumId }); + .where('"albums_assets"."albumsId" = :albumId', { albumId }) + .andWhere('"albums_assets"."assetsId" IN (:...assetIds)', { assetIds }) + .getRawMany<{ assetId: string }>(); - if (!assetIds?.length) { - const result = await query.getRawMany(); - return new Set(result.map((row) => row['assetId'])); - } - - return Promise.all( - _.chunk(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) => - query - .andWhere('"albums_assets"."assetsId" IN (:...assetIds)', { assetIds: idChunk }) - .getRawMany() - .then((result) => new Set(result.map((row) => row['assetId']))), - ), - ).then((results) => setUnion(...results)); + return new Set(results.map(({ assetId }) => assetId)); } @GenerateSql({ params: [{ albumId: DummyValue.UUID, assetId: DummyValue.UUID }] }) diff --git a/server/src/repositories/memory.repository.ts b/server/src/repositories/memory.repository.ts index a68d97152b..e9b4532fe9 100644 --- a/server/src/repositories/memory.repository.ts +++ b/server/src/repositories/memory.repository.ts @@ -61,9 +61,9 @@ export class MemoryRepository implements IMemoryRepository { .from('memories_assets_assets', 'memories_assets') .where('"memories_assets"."memoriesId" = :memoryId', { memoryId: id }) .andWhere('memories_assets.assetsId IN (:...assetIds)', { assetIds }) - .getRawMany(); + .getRawMany<{ assetId: string }>(); - return new Set(results.map((row) => row['assetId'])); + return new Set(results.map(({ assetId }) => assetId)); } @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })