refactor: use cursors instead of pages

This commit is contained in:
izzy
2026-01-06 15:49:36 +00:00
parent 06ee275202
commit d189722bbf
12 changed files with 100 additions and 115 deletions

View File

@@ -130,6 +130,14 @@ const create = (path: string, up: string[], down: string[]) => {
const compare = async () => {
const configRepository = new ConfigRepository();
const { database } = configRepository.getEnv();
database.config = {
connectionType: 'parts',
database: 'immich',
host: 'database',
password: 'postgres',
username: 'postgres',
port: 5432,
};
const db = postgres(asPostgresConnectionConfig(database.config));
const source = schemaFromCode({ overrides: true, namingStrategy: 'default' });

View File

@@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsInt, IsOptional, Min } from 'class-validator';
import { IsInt, IsOptional, IsUUID, Min } from 'class-validator';
import { IntegrityReportType } from 'src/enum';
import { ValidateEnum } from 'src/validation';
@@ -17,17 +17,15 @@ export class IntegrityGetReportDto {
@ValidateEnum({ enum: IntegrityReportType, name: 'IntegrityReportType' })
type!: IntegrityReportType;
@IsInt()
@Min(1)
@IsOptional()
@Type(() => Number)
page?: number;
@IsUUID()
cursor?: string;
@IsInt()
@Min(1)
@IsOptional()
@Type(() => Number)
size?: number;
limit?: number;
}
export class IntegrityDeleteReportDto {
@@ -44,5 +42,5 @@ class IntegrityReportDto {
export class IntegrityReportResponseDto {
items!: IntegrityReportDto[];
hasNextPage!: boolean;
nextCursor?: string;
}

View File

@@ -31,16 +31,16 @@ select
"type",
"path",
"assetId",
"fileAssetId"
"fileAssetId",
"createdAt"
from
"integrity_report"
where
"type" = $1
and "createdAt" <= $2
order by
"createdAt" desc
limit
$2
offset
$3
-- IntegrityRepository.getAssetPathsByPaths

View File

@@ -5,11 +5,10 @@ import { DummyValue, GenerateSql } from 'src/decorators';
import { IntegrityReportType } from 'src/enum';
import { DB } from 'src/schema';
import { IntegrityReportTable } from 'src/schema/tables/integrity-report.table';
import { paginationHelper } from 'src/utils/pagination';
export interface ReportPaginationOptions {
page: number;
size: number;
cursor?: string;
limit: number;
}
@Injectable()
@@ -64,18 +63,21 @@ export class IntegrityRepository {
.executeTakeFirstOrThrow();
}
@GenerateSql({ params: [{ page: 1, size: 100 }, DummyValue.STRING] })
@GenerateSql({ params: [{ cursor: DummyValue.NUMBER, limit: 100 }, DummyValue.STRING] })
async getIntegrityReports(pagination: ReportPaginationOptions, type: IntegrityReportType) {
const items = await this.db
.selectFrom('integrity_report')
.select(['id', 'type', 'path', 'assetId', 'fileAssetId'])
.select(['id', 'type', 'path', 'assetId', 'fileAssetId', 'createdAt'])
.where('type', '=', type)
.orderBy('createdAt', 'desc')
.limit(pagination.size + 1)
.offset((pagination.page - 1) * pagination.size)
.$if(pagination.cursor !== undefined, (eb) => eb.where('id', '<=', pagination.cursor!))
.orderBy('id', 'desc')
.limit(pagination.limit + 1)
.execute();
return paginationHelper(items, pagination.size);
return {
items: items.slice(0, pagination.limit),
nextCursor: items[pagination.limit]?.id,
};
}
@GenerateSql({ params: [DummyValue.STRING] })

View File

@@ -2,7 +2,7 @@ import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await sql`CREATE TABLE "integrity_report" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"id" uuid NOT NULL DEFAULT immich_uuid_v7(),
"type" character varying NOT NULL,
"path" character varying NOT NULL,
"createdAt" timestamp with time zone NOT NULL DEFAULT now(),

View File

@@ -1,21 +1,13 @@
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
import { IntegrityReportType } from 'src/enum';
import { AssetFileTable } from 'src/schema/tables/asset-file.table';
import { AssetTable } from 'src/schema/tables/asset.table';
import {
Column,
CreateDateColumn,
ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn,
Table,
Timestamp,
Unique,
} from 'src/sql-tools';
import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp, Unique } from 'src/sql-tools';
@Table('integrity_report')
@Unique({ columns: ['type', 'path'] })
export class IntegrityReportTable {
@PrimaryGeneratedColumn()
@PrimaryGeneratedUuidV7Column()
id!: Generated<string>;
@Column()

View File

@@ -140,7 +140,7 @@ export class IntegrityService extends BaseService {
}
async getIntegrityReport(dto: IntegrityGetReportDto): Promise<IntegrityReportResponseDto> {
return this.integrityRepository.getIntegrityReports({ page: dto.page || 1, size: dto.size || 100 }, dto.type);
return this.integrityRepository.getIntegrityReports({ cursor: dto.cursor, limit: dto.limit || 100 }, dto.type);
}
getIntegrityReportCsv(type: IntegrityReportType): Readable {