diff --git a/e2e/src/api/specs/library.e2e-spec.ts b/e2e/src/api/specs/library.e2e-spec.ts index 2f6274d1fc..e04b909c16 100644 --- a/e2e/src/api/specs/library.e2e-spec.ts +++ b/e2e/src/api/specs/library.e2e-spec.ts @@ -851,4 +851,26 @@ describe('/libraries', () => { expect(existsSync(`${testAssetDir}/temp/directoryB/assetB.png`)).toBe(true); }); }); + + describe('POST /search/metadata', () => { + it('should search by originalPath', async () => { + const directory = `some-61498-directory`; + const infix = 'me-61498-di'; + + utils.createImageFile(`${testAssetDir}/temp/${directory}/assetZ.jpg`); + await scan(admin.accessToken, library.id); + await utils.waitForWebsocketEvent({ event: 'assetUpload', total: 1 }); + + const { status, body } = await request(app) + .post('/search/metadata') + .send({ originalPath: infix }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body.assets).toBeDefined(); + expect(Array.isArray(body.assets.items)).toBe(true); + expect(body.assets.items).toHaveLength(1); + expect(body.assets.items[0]).toEqual(expect.objectContaining({ originalFileName: 'assetZ.jpg' })); + }); + }); }); diff --git a/e2e/src/api/specs/search.e2e-spec.ts b/e2e/src/api/specs/search.e2e-spec.ts index beeaf1cc01..c3e41caafc 100644 --- a/e2e/src/api/specs/search.e2e-spec.ts +++ b/e2e/src/api/specs/search.e2e-spec.ts @@ -288,13 +288,6 @@ describe('/search', () => { should: 'should search by takenAfter (no results)', deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }), }, - // { - // should: 'should search by originalPath', - // deferred: () => ({ - // dto: { originalPath: asset1.originalPath }, - // assets: [asset1], - // }), - // }, { should: 'should search by originalFilename', deferred: () => ({ diff --git a/server/src/entities/asset.entity.ts b/server/src/entities/asset.entity.ts index 9ebf9364d1..76d0e6f17a 100644 --- a/server/src/entities/asset.entity.ts +++ b/server/src/entities/asset.entity.ts @@ -44,7 +44,8 @@ export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum'; @Index('IDX_originalPath_libraryId', ['originalPath', 'libraryId']) @Index('IDX_asset_id_stackId', ['id', 'stackId']) @Index('idx_originalFileName_trigram', { synchronize: false }) -// For all assets, each originalpath must be unique per user and library +@Index('idx_originalPath_trigram', { synchronize: false }) +// For all assets, each originalPath must be unique per user and library export class AssetEntity { @PrimaryGeneratedColumn('uuid') id!: string; diff --git a/server/src/migrations/1724231348454-AddAssetOriginalPathTrigramIndex.ts b/server/src/migrations/1724231348454-AddAssetOriginalPathTrigramIndex.ts new file mode 100644 index 0000000000..fd8e7b83e7 --- /dev/null +++ b/server/src/migrations/1724231348454-AddAssetOriginalPathTrigramIndex.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddAssetOriginalPathTrigramIndex1724231348454 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE INDEX idx_originalPath_trigram ON assets USING gin (f_unaccent("originalPath") gin_trgm_ops)`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "idx_originalPath_trigram"`); + } + +} diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql index e9e94400ad..722764f654 100644 --- a/server/src/queries/search.repository.sql +++ b/server/src/queries/search.repository.sql @@ -75,7 +75,6 @@ FROM "asset"."fileCreatedAt" >= $1 AND "exifInfo"."lensModel" = $2 AND 1 = 1 - AND 1 = 1 AND ( "asset"."isFavorite" = $3 AND "asset"."isArchived" = $4 @@ -169,7 +168,6 @@ WHERE "asset"."fileCreatedAt" >= $1 AND "exifInfo"."lensModel" = $2 AND 1 = 1 - AND 1 = 1 AND ( "asset"."isFavorite" = $3 AND "asset"."isArchived" = $4 diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index f3232eb78b..c357b99d4a 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -71,8 +71,15 @@ export function searchAssetBuilder( builder.andWhere(`${builder.alias}.ownerId IN (:...userIds)`, { userIds: options.userIds }); } - const path = _.pick(options, ['encodedVideoPath', 'originalPath']); - builder.andWhere(_.omitBy(path, _.isUndefined)); + if (options.encodedVideoPath) { + builder.andWhere({ encodedVideoPath: options.encodedVideoPath }); + } + + if (options.originalPath) { + builder.andWhere(`f_unaccent(${builder.alias}.originalPath) ILIKE f_unaccent(:originalPath)`, { + originalPath: `%${options.originalPath}%`, + }); + } if (options.originalFileName) { builder.andWhere(`f_unaccent(${builder.alias}.originalFileName) ILIKE f_unaccent(:originalFileName)`, {