use min year for memory time series, sql generation fixes

This commit is contained in:
mertalev 2024-12-19 20:00:42 -05:00
parent 38a82d39d3
commit 4731f271e2
No known key found for this signature in database
GPG Key ID: 3A2B5BFC678DBC80
10 changed files with 177 additions and 132 deletions

View File

@ -450,19 +450,6 @@ WHERE
ORDER BY ORDER BY
"AlbumEntity"."createdAt" DESC "AlbumEntity"."createdAt" DESC
-- AlbumRepository.removeAsset
DELETE FROM "albums_assets_assets"
WHERE
"albums_assets_assets"."assetsId" = $1
-- AlbumRepository.removeAssetIds
DELETE FROM "albums_assets_assets"
WHERE
(
"albumsId" = $1
AND "assetsId" IN ($2)
)
-- AlbumRepository.getAssetIds -- AlbumRepository.getAssetIds
SELECT SELECT
"albums_assets"."assetsId" AS "assetId" "albums_assets"."assetsId" AS "assetId"
@ -471,52 +458,3 @@ FROM
WHERE WHERE
"albums_assets"."albumsId" = $1 "albums_assets"."albumsId" = $1
AND "albums_assets"."assetsId" IN ($2) AND "albums_assets"."assetsId" IN ($2)
-- AlbumRepository.addAssetIds
INSERT INTO
"albums_assets_assets" ("albumsId", "assetsId")
VALUES
($1, $2)
-- AlbumRepository.updateThumbnails
UPDATE "albums"
SET
"albumThumbnailAssetId" = (
SELECT
"album_assets"."assetsId"
FROM
"albums_assets_assets" "album_assets"
INNER JOIN "assets" "assets" ON "album_assets"."assetsId" = "assets"."id"
AND "assets"."deletedAt" IS NULL
WHERE
"album_assets"."albumsId" = "albums"."id"
ORDER BY
"assets"."fileCreatedAt" DESC
LIMIT
1
),
"updatedAt" = CURRENT_TIMESTAMP
WHERE
"albums"."albumThumbnailAssetId" IS NULL
AND EXISTS (
SELECT
1
FROM
"albums_assets_assets" "album_assets"
INNER JOIN "assets" "assets" ON "album_assets"."assetsId" = "assets"."id"
AND "assets"."deletedAt" IS NULL
WHERE
"album_assets"."albumsId" = "albums"."id"
)
OR "albums"."albumThumbnailAssetId" IS NOT NULL
AND NOT EXISTS (
SELECT
1
FROM
"albums_assets_assets" "album_assets"
INNER JOIN "assets" "assets" ON "album_assets"."assetsId" = "assets"."id"
AND "assets"."deletedAt" IS NULL
WHERE
"album_assets"."albumsId" = "albums"."id"
AND "albums"."albumThumbnailAssetId" = "album_assets"."assetsId"
)

View File

@ -9,12 +9,16 @@ with
make_date(year::int, $1::int, $2::int) as "date" make_date(year::int, $1::int, $2::int) as "date"
from from
generate_series( generate_series(
$3, (
extract( select
year date_part(
'year',
min((("localDateTime" at time zone 'UTC')::date))
)::int
from from
current_date assets
) - 1 ),
date_part('year', current_date)::int - 1
) as "year" ) as "year"
) )
select select
@ -31,20 +35,20 @@ with
where where
"asset_job_status"."previewAt" is not null "asset_job_status"."previewAt" is not null
and (assets."localDateTime" at time zone 'UTC')::date = today.date and (assets."localDateTime" at time zone 'UTC')::date = today.date
and "assets"."ownerId" = any ($4::uuid []) and "assets"."ownerId" = any ($3::uuid [])
and "assets"."isVisible" = $5 and "assets"."isVisible" = $4
and "assets"."isArchived" = $6 and "assets"."isArchived" = $5
and exists ( and exists (
select select
from from
"asset_files" "asset_files"
where where
"assetId" = "assets"."id" "assetId" = "assets"."id"
and "asset_files"."type" = $7 and "asset_files"."type" = $6
) )
and "assets"."deletedAt" is null and "assets"."deletedAt" is null
limit limit
$8 $7
) as "a" on true ) as "a" on true
inner join "exif" on "a"."id" = "exif"."assetId" inner join "exif" on "a"."id" = "exif"."assetId"
) )
@ -60,7 +64,7 @@ group by
order by order by
("localDateTime" at time zone 'UTC')::date desc ("localDateTime" at time zone 'UTC')::date desc
limit limit
$9 $8
-- AssetRepository.getByIds -- AssetRepository.getByIds
select select
@ -208,6 +212,17 @@ where
limit limit
$4 $4
-- AssetRepository.getByChecksums
select
"id",
"checksum",
"deletedAt"
from
"assets"
where
"ownerId" = $1::uuid
and "checksum" in ($2)
-- AssetRepository.getUploadAssetIdByChecksum -- AssetRepository.getUploadAssetIdByChecksum
select select
"id" "id"
@ -274,6 +289,52 @@ where
order by order by
"assets"."localDateTime" desc "assets"."localDateTime" desc
-- AssetRepository.getDuplicates
with
"duplicates" as (
select
"duplicateId",
jsonb_agg("assets") as "assets"
from
"assets"
where
"ownerId" = $1::uuid
and "duplicateId" is not null
and "deletedAt" is null
and "isVisible" = $2
group by
"duplicateId"
),
"unique" as (
select
"duplicateId"
from
"duplicates"
where
jsonb_array_length("assets") = $3
),
"removed_unique" as (
update "assets"
set
"duplicateId" = $4
from
"unique"
where
"assets"."duplicateId" = "unique"."duplicateId"
)
select
*
from
"duplicates"
where
not exists (
select
from
"unique"
where
"unique"."duplicateId" = "duplicates"."duplicateId"
)
-- AssetRepository.getAssetIdByCity -- AssetRepository.getAssetIdByCity
with with
"cities" as ( "cities" as (
@ -317,3 +378,23 @@ order by
"id" asc "id" asc
limit limit
$5 $5
-- AssetRepository.getChangedDeltaSync
select
"assets".*,
(
select
count(*) as "stackedAssetsCount"
from
"asset_stack"
where
"asset_stack"."id" = "assets"."stackId"
) as "stackedAssetsCount"
from
"assets"
where
"ownerId" = any ($1::uuid [])
and "isVisible" = $2
and "updatedAt" > $3
limit
$4

View File

@ -8,17 +8,3 @@ FROM
WHERE WHERE
"memories_assets"."memoriesId" = $1 "memories_assets"."memoriesId" = $1
AND "memories_assets"."assetsId" IN ($2) AND "memories_assets"."assetsId" IN ($2)
-- MemoryRepository.addAssetIds
INSERT INTO
"memories_assets_assets" ("memoriesId", "assetsId")
VALUES
($1, $2)
-- MemoryRepository.removeAssetIds
DELETE FROM "memories_assets_assets"
WHERE
(
"memoriesId" = $1
AND "assetsId" IN ($2)
)

View File

@ -20,6 +20,47 @@ limit
offset offset
$7 $7
-- SearchRepository.searchRandom
(
select
"assets".*
from
"assets"
inner join "exif" on "assets"."id" = "exif"."assetId"
where
"assets"."fileCreatedAt" >= $1
and "exif"."lensModel" = $2
and "assets"."ownerId" = any ($3::uuid [])
and "assets"."isFavorite" = $4
and "assets"."isArchived" = $5
and "assets"."deletedAt" is null
and "assets"."id" < $6
order by
"assets"."id"
limit
$7
)
union all
(
select
"assets".*
from
"assets"
inner join "exif" on "assets"."id" = "exif"."assetId"
where
"assets"."fileCreatedAt" >= $8
and "exif"."lensModel" = $9
and "assets"."ownerId" = any ($10::uuid [])
and "assets"."isFavorite" = $11
and "assets"."isArchived" = $12
and "assets"."deletedAt" is null
and "assets"."id" > $13
order by
"assets"."id"
limit
$14
)
-- SearchRepository.searchSmart -- SearchRepository.searchSmart
select select
"assets".* "assets".*
@ -41,6 +82,34 @@ limit
offset offset
$8 $8
-- SearchRepository.searchDuplicates
with
"cte" as (
select
"assets"."id" as "assetId",
"assets"."duplicateId",
smart_search.embedding <= > $1::vector as "distance"
from
"assets"
inner join "smart_search" on "assets"."id" = "smart_search"."assetId"
where
"assets"."ownerId" = any ($2::uuid [])
and "assets"."deletedAt" is null
and "assets"."isVisible" = $3
and "assets"."type" = $4
and "assets"."id" != $5::uuid
order by
smart_search.embedding <= > $6::vector
limit
$7
)
select
*
from
"cte"
where
"cte"."distance" <= $8
-- SearchRepository.searchFaces -- SearchRepository.searchFaces
with with
"cte" as ( "cte" as (

View File

@ -8,23 +8,3 @@ FROM
WHERE WHERE
"tag_asset"."tagsId" = $1 "tag_asset"."tagsId" = $1
AND "tag_asset"."assetsId" IN ($2) AND "tag_asset"."assetsId" IN ($2)
-- TagRepository.addAssetIds
INSERT INTO
"tag_asset" ("assetsId", "tagsId")
VALUES
($1, $2)
-- TagRepository.removeAssetIds
DELETE FROM "tag_asset"
WHERE
(
"tagsId" = $1
AND "assetsId" IN ($2)
)
-- TagRepository.upsertAssetIds
INSERT INTO
"tag_asset" ("assetsId", "tagsId")
VALUES
($1, $2)

View File

@ -153,7 +153,6 @@ export class AlbumRepository implements IAlbumRepository {
await this.repository.delete({ ownerId: userId }); await this.repository.delete({ ownerId: userId });
} }
@GenerateSql({ params: [DummyValue.UUID] })
async removeAsset(assetId: string): Promise<void> { async removeAsset(assetId: string): Promise<void> {
// Using dataSource, because there is no direct access to albums_assets_assets. // Using dataSource, because there is no direct access to albums_assets_assets.
await this.dataSource await this.dataSource
@ -164,7 +163,6 @@ export class AlbumRepository implements IAlbumRepository {
.execute(); .execute();
} }
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
@Chunked({ paramIndex: 1 }) @Chunked({ paramIndex: 1 })
async removeAssetIds(albumId: string, assetIds: string[]): Promise<void> { async removeAssetIds(albumId: string, assetIds: string[]): Promise<void> {
if (assetIds.length === 0) { if (assetIds.length === 0) {
@ -207,7 +205,6 @@ export class AlbumRepository implements IAlbumRepository {
return new Set(results.map(({ assetId }) => assetId)); return new Set(results.map(({ assetId }) => assetId));
} }
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
async addAssetIds(albumId: string, assetIds: string[]): Promise<void> { async addAssetIds(albumId: string, assetIds: string[]): Promise<void> {
await this.addAssets(this.dataSource.manager, albumId, assetIds); await this.addAssets(this.dataSource.manager, albumId, assetIds);
} }
@ -272,7 +269,6 @@ export class AlbumRepository implements IAlbumRepository {
* *
* @returns Amount of updated album thumbnails or undefined when unknown * @returns Amount of updated album thumbnails or undefined when unknown
*/ */
@GenerateSql()
async updateThumbnails(): Promise<number | undefined> { async updateThumbnails(): Promise<number | undefined> {
// Subquery for getting a new thumbnail. // Subquery for getting a new thumbnail.

View File

@ -92,15 +92,18 @@ 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<DayOfYearAssets[]> { getByDayOfYear(ownerIds: string[], { day, month }: MonthDay): Promise<DayOfYearAssets[]> {
// TODO: CREATE INDEX idx_local_date_time ON public.assets ((("localDateTime" at time zone 'UTC')::date));
// TODO: drop IDX_day_of_month and IDX_month
return this.db return this.db
.with('res', (qb) => .with('res', (qb) =>
qb qb
.with('today', (qb) => .with('today', (qb) =>
qb qb
.selectFrom((eb) => .selectFrom((eb) =>
eb.fn('generate_series', [eb.val(1970), sql`extract(year from current_date) - 1`]).as('year'), eb
.fn('generate_series', [
sql`(select date_part('year', min((("localDateTime" at time zone 'UTC')::date)))::int from assets)`,
sql`date_part('year', current_date)::int - 1`,
])
.as('year'),
) )
.select((eb) => eb.fn('make_date', [sql`year::int`, sql`${month}::int`, sql`${day}::int`]).as('date')), .select((eb) => eb.fn('make_date', [sql`year::int`, sql`${month}::int`, sql`${day}::int`]).as('date')),
) )
@ -348,7 +351,7 @@ export class AssetRepository implements IAssetRepository {
.executeTakeFirst() as Promise<AssetEntity | undefined>; .executeTakeFirst() as Promise<AssetEntity | undefined>;
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.BUFFER] }) @GenerateSql({ params: [DummyValue.UUID, [DummyValue.BUFFER]] })
getByChecksums(userId: string, checksums: Buffer[]): Promise<AssetEntity[]> { getByChecksums(userId: string, checksums: Buffer[]): Promise<AssetEntity[]> {
return this.db return this.db
.selectFrom('assets') .selectFrom('assets')
@ -576,7 +579,6 @@ export class AssetRepository implements IAssetRepository {
@GenerateSql({ params: [DummyValue.TIME_BUCKET, { size: TimeBucketSize.MONTH }] }) @GenerateSql({ params: [DummyValue.TIME_BUCKET, { size: TimeBucketSize.MONTH }] })
async getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]> { async getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]> {
// TODO: CREATE INDEX idx_local_date_time_month ON public.assets (date_trunc('MONTH', "localDateTime" at time zone 'UTC'));
return hasPeople(this.db, options.personId ? [options.personId] : undefined) return hasPeople(this.db, options.personId ? [options.personId] : undefined)
.selectAll('assets') .selectAll('assets')
.$call(withExif) .$call(withExif)
@ -601,7 +603,7 @@ export class AssetRepository implements IAssetRepository {
.execute() as any as Promise<AssetEntity[]>; .execute() as any as Promise<AssetEntity[]>;
} }
@GenerateSql({ params: [{ userIds: [DummyValue.UUID, DummyValue.UUID] }] }) @GenerateSql({ params: [DummyValue.UUID] })
getDuplicates(userId: string): Promise<DuplicateGroup[]> { getDuplicates(userId: string): Promise<DuplicateGroup[]> {
return ( return (
this.db this.db
@ -692,7 +694,7 @@ export class AssetRepository implements IAssetRepository {
.execute() as any as Promise<AssetEntity[]>; .execute() as any as Promise<AssetEntity[]>;
} }
@GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE }] }) @GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE, limit: 100 }] })
async getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]> { async getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]> {
return this.db return this.db
.selectFrom('assets') .selectFrom('assets')
@ -711,7 +713,6 @@ export class AssetRepository implements IAssetRepository {
.execute() as any as Promise<AssetEntity[]>; .execute() as any as Promise<AssetEntity[]>;
} }
@GenerateSql({ params: [{ assetId: DummyValue.UUID, type: AssetFileType.PREVIEW, path: '/path/to/file' }] })
async upsertFile(file: Pick<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>): Promise<void> { async upsertFile(file: Pick<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>): Promise<void> {
const value = { ...file, assetId: asUuid(file.assetId) }; const value = { ...file, assetId: asUuid(file.assetId) };
await this.db await this.db
@ -725,7 +726,6 @@ export class AssetRepository implements IAssetRepository {
.execute(); .execute();
} }
@GenerateSql({ params: [{ assetId: DummyValue.UUID, type: AssetFileType.PREVIEW, path: '/path/to/file' }] })
async upsertFiles(files: Pick<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>[]): Promise<void> { async upsertFiles(files: Pick<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>[]): Promise<void> {
if (files.length === 0) { if (files.length === 0) {
return; return;

View File

@ -64,7 +64,6 @@ export class MemoryRepository implements IMemoryRepository {
return new Set(results.map(({ assetId }) => assetId)); return new Set(results.map(({ assetId }) => assetId));
} }
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
async addAssetIds(id: string, assetIds: string[]): Promise<void> { async addAssetIds(id: string, assetIds: string[]): Promise<void> {
await this.dataSource await this.dataSource
.createQueryBuilder() .createQueryBuilder()
@ -74,7 +73,6 @@ export class MemoryRepository implements IMemoryRepository {
.execute(); .execute();
} }
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
@Chunked({ paramIndex: 1 }) @Chunked({ paramIndex: 1 })
async removeAssetIds(id: string, assetIds: string[]): Promise<void> { async removeAssetIds(id: string, assetIds: string[]): Promise<void> {
await this.dataSource await this.dataSource

View File

@ -6,6 +6,7 @@ import { DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetEntity, searchAssetBuilder } from 'src/entities/asset.entity'; import { AssetEntity, searchAssetBuilder } from 'src/entities/asset.entity';
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity'; import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
import { AssetType } from 'src/enum';
import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { import {
AssetDuplicateSearch, AssetDuplicateSearch,
@ -71,12 +72,9 @@ export class SearchRepository implements ISearchRepository {
searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]> { searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]> {
const uuid = randomUUID(); const uuid = randomUUID();
const builder = searchAssetBuilder(this.db, options); const builder = searchAssetBuilder(this.db, options);
return builder const lessThan = builder.where('assets.id', '<', uuid).orderBy('assets.id').limit(size);
.where('assets.id', '>', uuid) const greaterThan = builder.where('assets.id', '>', uuid).orderBy('assets.id').limit(size);
.orderBy('assets.id') return sql`${lessThan} union all ${greaterThan}`.execute(this.db) as any as Promise<AssetEntity[]>;
.limit(size)
.unionAll(() => builder.where('assets.id', '<', uuid).orderBy('assets.id').limit(size))
.execute() as any as Promise<AssetEntity[]>;
} }
@GenerateSql({ @GenerateSql({
@ -112,8 +110,10 @@ export class SearchRepository implements ISearchRepository {
@GenerateSql({ @GenerateSql({
params: [ params: [
{ {
assetId: DummyValue.UUID,
embedding: Array.from({ length: 512 }, Math.random), embedding: Array.from({ length: 512 }, Math.random),
maxDistance: 0.6, maxDistance: 0.6,
type: AssetType.IMAGE,
userIds: [DummyValue.UUID], userIds: [DummyValue.UUID],
}, },
], ],
@ -134,7 +134,7 @@ export class SearchRepository implements ISearchRepository {
.where('assets.deletedAt', 'is', null) .where('assets.deletedAt', 'is', null)
.where('assets.isVisible', '=', true) .where('assets.isVisible', '=', true)
.where('assets.type', '=', type) .where('assets.type', '=', type)
.where('assets.id', '!=', assetId) .where('assets.id', '!=', asUuid(assetId))
.orderBy(sql`smart_search.embedding <=> ${vector}`) .orderBy(sql`smart_search.embedding <=> ${vector}`)
.limit(64), .limit(64),
) )

View File

@ -108,7 +108,6 @@ export class TagRepository implements ITagRepository {
return new Set(results.map(({ assetId }) => assetId)); return new Set(results.map(({ assetId }) => assetId));
} }
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
async addAssetIds(tagId: string, assetIds: string[]): Promise<void> { async addAssetIds(tagId: string, assetIds: string[]): Promise<void> {
if (assetIds.length === 0) { if (assetIds.length === 0) {
return; return;
@ -122,7 +121,6 @@ export class TagRepository implements ITagRepository {
.execute(); .execute();
} }
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
@Chunked({ paramIndex: 1 }) @Chunked({ paramIndex: 1 })
async removeAssetIds(tagId: string, assetIds: string[]): Promise<void> { async removeAssetIds(tagId: string, assetIds: string[]): Promise<void> {
if (assetIds.length === 0) { if (assetIds.length === 0) {
@ -140,7 +138,6 @@ export class TagRepository implements ITagRepository {
.execute(); .execute();
} }
@GenerateSql({ params: [[{ assetId: DummyValue.UUID, tagId: DummyValue.UUID }]] })
@Chunked() @Chunked()
async upsertAssetIds(items: AssetTagItem[]): Promise<AssetTagItem[]> { async upsertAssetIds(items: AssetTagItem[]): Promise<AssetTagItem[]> {
if (items.length === 0) { if (items.length === 0) {