1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

refactor(server): move filters to getByDayOfYear query (#14628)

move filters to getByDayOfYear query
This commit is contained in:
Mert 2024-12-10 16:22:47 -05:00 committed by GitHub
parent 25ca3b1124
commit 9eff1c4b34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 55 additions and 38 deletions

View file

@ -146,6 +146,11 @@ export interface UpsertFileOptions {
export type AssetPathEntity = Pick<AssetEntity, 'id' | 'originalPath' | 'isOffline'>; export type AssetPathEntity = Pick<AssetEntity, 'id' | 'originalPath' | 'isOffline'>;
export interface DayOfYearAssets {
yearsAgo: number;
assets: AssetEntity[];
}
export const IAssetRepository = 'IAssetRepository'; export const IAssetRepository = 'IAssetRepository';
export interface IAssetRepository { export interface IAssetRepository {
@ -156,7 +161,7 @@ export interface IAssetRepository {
select?: FindOptionsSelect<AssetEntity>, select?: FindOptionsSelect<AssetEntity>,
): Promise<AssetEntity[]>; ): Promise<AssetEntity[]>;
getByIdsWithAllRelations(ids: string[]): Promise<AssetEntity[]>; getByIdsWithAllRelations(ids: string[]): Promise<AssetEntity[]>;
getByDayOfYear(ownerIds: string[], monthDay: MonthDay): Promise<AssetEntity[]>; getByDayOfYear(ownerIds: string[], monthDay: MonthDay): Promise<DayOfYearAssets[]>;
getByChecksum(options: { ownerId: string; checksum: Buffer; libraryId?: string }): Promise<AssetEntity | null>; getByChecksum(options: { ownerId: string; checksum: Buffer; libraryId?: string }): Promise<AssetEntity | null>;
getByChecksums(userId: string, checksums: Buffer[]): Promise<AssetEntity[]>; getByChecksums(userId: string, checksums: Buffer[]): Promise<AssetEntity[]>;
getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise<string | undefined>; getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise<string | undefined>;

View file

@ -68,22 +68,19 @@ SELECT
FROM FROM
"assets" "entity" "assets" "entity"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "entity"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "entity"."id"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "entity"."id" INNER JOIN "asset_files" "files" ON "files"."assetId" = "entity"."id"
WHERE WHERE
( (
"entity"."ownerId" IN ($1) "files"."type" = $1
AND "entity"."isVisible" = true
AND "entity"."isArchived" = false
AND EXTRACT( AND EXTRACT(
DAY YEAR
FROM
CURRENT_DATE AT TIME ZONE 'UTC'
) - EXTRACT(
YEAR
FROM FROM
"entity"."localDateTime" AT TIME ZONE 'UTC' "entity"."localDateTime" AT TIME ZONE 'UTC'
) = $2 ) > 0
AND EXTRACT(
MONTH
FROM
"entity"."localDateTime" AT TIME ZONE 'UTC'
) = $3
) )
AND ("entity"."deletedAt" IS NULL) AND ("entity"."deletedAt" IS NULL)
ORDER BY ORDER BY

View file

@ -17,6 +17,7 @@ import {
AssetUpdateAllOptions, AssetUpdateAllOptions,
AssetUpdateDuplicateOptions, AssetUpdateDuplicateOptions,
AssetUpdateOptions, AssetUpdateOptions,
DayOfYearAssets,
IAssetRepository, IAssetRepository,
LivePhotoSearchOptions, LivePhotoSearchOptions,
MonthDay, MonthDay,
@ -74,8 +75,8 @@ export class AssetRepository implements IAssetRepository {
} }
@GenerateSql({ params: [[DummyValue.UUID], { day: 1, month: 1 }] }) @GenerateSql({ params: [[DummyValue.UUID], { day: 1, month: 1 }] })
getByDayOfYear(ownerIds: string[], { day, month }: MonthDay): Promise<AssetEntity[]> { async getByDayOfYear(ownerIds: string[], { day, month }: MonthDay): Promise<DayOfYearAssets[]> {
return this.repository const assets = await this.repository
.createQueryBuilder('entity') .createQueryBuilder('entity')
.where( .where(
`entity.ownerId IN (:...ownerIds) `entity.ownerId IN (:...ownerIds)
@ -90,9 +91,25 @@ export class AssetRepository implements IAssetRepository {
}, },
) )
.leftJoinAndSelect('entity.exifInfo', 'exifInfo') .leftJoinAndSelect('entity.exifInfo', 'exifInfo')
.leftJoinAndSelect('entity.files', 'files') .innerJoinAndSelect('entity.files', 'files')
.where('files.type = :type', { type: AssetFileType.THUMBNAIL })
.andWhere(
`EXTRACT(YEAR FROM CURRENT_DATE AT TIME ZONE 'UTC') - EXTRACT(YEAR FROM entity.localDateTime AT TIME ZONE 'UTC') > 0`,
)
.orderBy('entity.fileCreatedAt', 'ASC') .orderBy('entity.fileCreatedAt', 'ASC')
.getMany(); .getMany();
const groups: Record<number, DayOfYearAssets> = {};
const currentYear = new Date().getFullYear();
for (const asset of assets) {
const yearsAgo = currentYear - asset.localDateTime.getFullYear();
if (!groups[yearsAgo]) {
groups[yearsAgo] = { yearsAgo, assets: [] };
}
groups[yearsAgo].assets.push(asset);
}
return Object.values(groups);
} }
@GenerateSql({ params: [[DummyValue.UUID]] }) @GenerateSql({ params: [[DummyValue.UUID]] })

View file

@ -80,7 +80,20 @@ describe(AssetService.name, () => {
const image4 = { ...assetStub.image, localDateTime: new Date(2009, 1, 15) }; const image4 = { ...assetStub.image, localDateTime: new Date(2009, 1, 15) };
partnerMock.getAll.mockResolvedValue([]); partnerMock.getAll.mockResolvedValue([]);
assetMock.getByDayOfYear.mockResolvedValue([image1, image2, image3, image4]); assetMock.getByDayOfYear.mockResolvedValue([
{
yearsAgo: 1,
assets: [image1, image2],
},
{
yearsAgo: 9,
assets: [image3],
},
{
yearsAgo: 15,
assets: [image4],
},
]);
await expect(sut.getMemoryLane(authStub.admin, { day: 15, month: 1 })).resolves.toEqual([ await expect(sut.getMemoryLane(authStub.admin, { day: 15, month: 1 })).resolves.toEqual([
{ yearsAgo: 1, title: '1 year ago', assets: [mapAsset(image1), mapAsset(image2)] }, { yearsAgo: 1, title: '1 year ago', assets: [mapAsset(image1), mapAsset(image2)] },

View file

@ -43,28 +43,13 @@ export class AssetService extends BaseService {
}); });
const userIds = [auth.user.id, ...partnerIds]; const userIds = [auth.user.id, ...partnerIds];
const assets = await this.assetRepository.getByDayOfYear(userIds, dto); const groups = await this.assetRepository.getByDayOfYear(userIds, dto);
const assetsWithThumbnails = assets.filter(({ files }) => !!getAssetFiles(files).thumbnailFile); return groups.map(({ yearsAgo, assets }) => ({
const groups: Record<number, AssetEntity[]> = {}; yearsAgo,
const currentYear = new Date().getFullYear(); // TODO move this to clients
for (const asset of assetsWithThumbnails) { title: `${yearsAgo} year${yearsAgo > 1 ? 's' : ''} ago`,
const yearsAgo = currentYear - asset.localDateTime.getFullYear(); assets: assets.map((asset) => mapAsset(asset, { auth })),
if (!groups[yearsAgo]) { }));
groups[yearsAgo] = [];
}
groups[yearsAgo].push(asset);
}
return Object.keys(groups)
.map(Number)
.sort((a, b) => a - b)
.filter((yearsAgo) => yearsAgo > 0)
.map((yearsAgo) => ({
yearsAgo,
// TODO move this to clients
title: `${yearsAgo} year${yearsAgo > 1 ? 's' : ''} ago`,
assets: groups[yearsAgo].map((asset) => mapAsset(asset, { auth })),
}));
} }
async getStatistics(auth: AuthDto, dto: AssetStatsDto) { async getStatistics(auth: AuthDto, dto: AssetStatsDto) {