mirror of
https://github.com/immich-app/immich.git
synced 2025-01-24 04:32:45 +01:00
use min year for memory time series, sql generation fixes
This commit is contained in:
parent
38a82d39d3
commit
4731f271e2
10 changed files with 177 additions and 132 deletions
|
@ -450,19 +450,6 @@ WHERE
|
|||
ORDER BY
|
||||
"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
|
||||
SELECT
|
||||
"albums_assets"."assetsId" AS "assetId"
|
||||
|
@ -471,52 +458,3 @@ FROM
|
|||
WHERE
|
||||
"albums_assets"."albumsId" = $1
|
||||
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"
|
||||
)
|
||||
|
|
|
@ -9,12 +9,16 @@ with
|
|||
make_date(year::int, $1::int, $2::int) as "date"
|
||||
from
|
||||
generate_series(
|
||||
$3,
|
||||
extract(
|
||||
year
|
||||
(
|
||||
select
|
||||
date_part(
|
||||
'year',
|
||||
min((("localDateTime" at time zone 'UTC')::date))
|
||||
)::int
|
||||
from
|
||||
current_date
|
||||
) - 1
|
||||
assets
|
||||
),
|
||||
date_part('year', current_date)::int - 1
|
||||
) as "year"
|
||||
)
|
||||
select
|
||||
|
@ -31,20 +35,20 @@ with
|
|||
where
|
||||
"asset_job_status"."previewAt" is not null
|
||||
and (assets."localDateTime" at time zone 'UTC')::date = today.date
|
||||
and "assets"."ownerId" = any ($4::uuid [])
|
||||
and "assets"."isVisible" = $5
|
||||
and "assets"."isArchived" = $6
|
||||
and "assets"."ownerId" = any ($3::uuid [])
|
||||
and "assets"."isVisible" = $4
|
||||
and "assets"."isArchived" = $5
|
||||
and exists (
|
||||
select
|
||||
from
|
||||
"asset_files"
|
||||
where
|
||||
"assetId" = "assets"."id"
|
||||
and "asset_files"."type" = $7
|
||||
and "asset_files"."type" = $6
|
||||
)
|
||||
and "assets"."deletedAt" is null
|
||||
limit
|
||||
$8
|
||||
$7
|
||||
) as "a" on true
|
||||
inner join "exif" on "a"."id" = "exif"."assetId"
|
||||
)
|
||||
|
@ -60,7 +64,7 @@ group by
|
|||
order by
|
||||
("localDateTime" at time zone 'UTC')::date desc
|
||||
limit
|
||||
$9
|
||||
$8
|
||||
|
||||
-- AssetRepository.getByIds
|
||||
select
|
||||
|
@ -208,6 +212,17 @@ where
|
|||
limit
|
||||
$4
|
||||
|
||||
-- AssetRepository.getByChecksums
|
||||
select
|
||||
"id",
|
||||
"checksum",
|
||||
"deletedAt"
|
||||
from
|
||||
"assets"
|
||||
where
|
||||
"ownerId" = $1::uuid
|
||||
and "checksum" in ($2)
|
||||
|
||||
-- AssetRepository.getUploadAssetIdByChecksum
|
||||
select
|
||||
"id"
|
||||
|
@ -274,6 +289,52 @@ where
|
|||
order by
|
||||
"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
|
||||
with
|
||||
"cities" as (
|
||||
|
@ -317,3 +378,23 @@ order by
|
|||
"id" asc
|
||||
limit
|
||||
$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
|
||||
|
|
|
@ -8,17 +8,3 @@ FROM
|
|||
WHERE
|
||||
"memories_assets"."memoriesId" = $1
|
||||
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)
|
||||
)
|
||||
|
|
|
@ -20,6 +20,47 @@ limit
|
|||
offset
|
||||
$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
|
||||
select
|
||||
"assets".*
|
||||
|
@ -41,6 +82,34 @@ limit
|
|||
offset
|
||||
$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
|
||||
with
|
||||
"cte" as (
|
||||
|
|
|
@ -8,23 +8,3 @@ FROM
|
|||
WHERE
|
||||
"tag_asset"."tagsId" = $1
|
||||
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)
|
||||
|
|
|
@ -153,7 +153,6 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
await this.repository.delete({ ownerId: userId });
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async removeAsset(assetId: string): Promise<void> {
|
||||
// Using dataSource, because there is no direct access to albums_assets_assets.
|
||||
await this.dataSource
|
||||
|
@ -164,7 +163,6 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||
@Chunked({ paramIndex: 1 })
|
||||
async removeAssetIds(albumId: string, assetIds: string[]): Promise<void> {
|
||||
if (assetIds.length === 0) {
|
||||
|
@ -207,7 +205,6 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
return new Set(results.map(({ assetId }) => assetId));
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||
async addAssetIds(albumId: string, assetIds: string[]): Promise<void> {
|
||||
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
|
||||
*/
|
||||
@GenerateSql()
|
||||
async updateThumbnails(): Promise<number | undefined> {
|
||||
// Subquery for getting a new thumbnail.
|
||||
|
||||
|
|
|
@ -92,15 +92,18 @@ export class AssetRepository implements IAssetRepository {
|
|||
|
||||
@GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] })
|
||||
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
|
||||
.with('res', (qb) =>
|
||||
qb
|
||||
.with('today', (qb) =>
|
||||
qb
|
||||
.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')),
|
||||
)
|
||||
|
@ -348,7 +351,7 @@ export class AssetRepository implements IAssetRepository {
|
|||
.executeTakeFirst() as Promise<AssetEntity | undefined>;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.BUFFER] })
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.BUFFER]] })
|
||||
getByChecksums(userId: string, checksums: Buffer[]): Promise<AssetEntity[]> {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
|
@ -576,7 +579,6 @@ export class AssetRepository implements IAssetRepository {
|
|||
|
||||
@GenerateSql({ params: [DummyValue.TIME_BUCKET, { size: TimeBucketSize.MONTH }] })
|
||||
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)
|
||||
.selectAll('assets')
|
||||
.$call(withExif)
|
||||
|
@ -601,7 +603,7 @@ export class AssetRepository implements IAssetRepository {
|
|||
.execute() as any as Promise<AssetEntity[]>;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ userIds: [DummyValue.UUID, DummyValue.UUID] }] })
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getDuplicates(userId: string): Promise<DuplicateGroup[]> {
|
||||
return (
|
||||
this.db
|
||||
|
@ -692,7 +694,7 @@ export class AssetRepository implements IAssetRepository {
|
|||
.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[]> {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
|
@ -711,7 +713,6 @@ export class AssetRepository implements IAssetRepository {
|
|||
.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> {
|
||||
const value = { ...file, assetId: asUuid(file.assetId) };
|
||||
await this.db
|
||||
|
@ -725,7 +726,6 @@ export class AssetRepository implements IAssetRepository {
|
|||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ assetId: DummyValue.UUID, type: AssetFileType.PREVIEW, path: '/path/to/file' }] })
|
||||
async upsertFiles(files: Pick<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>[]): Promise<void> {
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
|
|
|
@ -64,7 +64,6 @@ export class MemoryRepository implements IMemoryRepository {
|
|||
return new Set(results.map(({ assetId }) => assetId));
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||
async addAssetIds(id: string, assetIds: string[]): Promise<void> {
|
||||
await this.dataSource
|
||||
.createQueryBuilder()
|
||||
|
@ -74,7 +73,6 @@ export class MemoryRepository implements IMemoryRepository {
|
|||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||
@Chunked({ paramIndex: 1 })
|
||||
async removeAssetIds(id: string, assetIds: string[]): Promise<void> {
|
||||
await this.dataSource
|
||||
|
|
|
@ -6,6 +6,7 @@ import { DB } from 'src/db';
|
|||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetEntity, searchAssetBuilder } from 'src/entities/asset.entity';
|
||||
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
|
||||
import { AssetType } from 'src/enum';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
import {
|
||||
AssetDuplicateSearch,
|
||||
|
@ -71,12 +72,9 @@ export class SearchRepository implements ISearchRepository {
|
|||
searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]> {
|
||||
const uuid = randomUUID();
|
||||
const builder = searchAssetBuilder(this.db, options);
|
||||
return builder
|
||||
.where('assets.id', '>', uuid)
|
||||
.orderBy('assets.id')
|
||||
.limit(size)
|
||||
.unionAll(() => builder.where('assets.id', '<', uuid).orderBy('assets.id').limit(size))
|
||||
.execute() as any as Promise<AssetEntity[]>;
|
||||
const lessThan = builder.where('assets.id', '<', uuid).orderBy('assets.id').limit(size);
|
||||
const greaterThan = builder.where('assets.id', '>', uuid).orderBy('assets.id').limit(size);
|
||||
return sql`${lessThan} union all ${greaterThan}`.execute(this.db) as any as Promise<AssetEntity[]>;
|
||||
}
|
||||
|
||||
@GenerateSql({
|
||||
|
@ -112,8 +110,10 @@ export class SearchRepository implements ISearchRepository {
|
|||
@GenerateSql({
|
||||
params: [
|
||||
{
|
||||
assetId: DummyValue.UUID,
|
||||
embedding: Array.from({ length: 512 }, Math.random),
|
||||
maxDistance: 0.6,
|
||||
type: AssetType.IMAGE,
|
||||
userIds: [DummyValue.UUID],
|
||||
},
|
||||
],
|
||||
|
@ -134,7 +134,7 @@ export class SearchRepository implements ISearchRepository {
|
|||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.type', '=', type)
|
||||
.where('assets.id', '!=', assetId)
|
||||
.where('assets.id', '!=', asUuid(assetId))
|
||||
.orderBy(sql`smart_search.embedding <=> ${vector}`)
|
||||
.limit(64),
|
||||
)
|
||||
|
|
|
@ -108,7 +108,6 @@ export class TagRepository implements ITagRepository {
|
|||
return new Set(results.map(({ assetId }) => assetId));
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||
async addAssetIds(tagId: string, assetIds: string[]): Promise<void> {
|
||||
if (assetIds.length === 0) {
|
||||
return;
|
||||
|
@ -122,7 +121,6 @@ export class TagRepository implements ITagRepository {
|
|||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||
@Chunked({ paramIndex: 1 })
|
||||
async removeAssetIds(tagId: string, assetIds: string[]): Promise<void> {
|
||||
if (assetIds.length === 0) {
|
||||
|
@ -140,7 +138,6 @@ export class TagRepository implements ITagRepository {
|
|||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[{ assetId: DummyValue.UUID, tagId: DummyValue.UUID }]] })
|
||||
@Chunked()
|
||||
async upsertAssetIds(items: AssetTagItem[]): Promise<AssetTagItem[]> {
|
||||
if (items.length === 0) {
|
||||
|
|
Loading…
Reference in a new issue