mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
fix(server): search statistics with personIds returns 500 (#25074)
The searchAssetBuilder was incorrectly adding withFacesAndPeople select when personIds was provided. This caused a SQL error because the subquery referenced asset.id which wasn't selected in statistics queries (only count(*) was selected). The fix removes personIds from the condition that triggers adding faces data to the select. The hasPeople filter (for personIds) is still applied correctly for filtering. Fixes #25003 Signed-off-by: majiayu000 <1835304752@qq.com>
This commit is contained in:
parent
f22affd836
commit
263f96da87
2 changed files with 38 additions and 2 deletions
|
|
@ -446,7 +446,7 @@ export function searchAssetBuilder(kysely: Kysely<DB>, options: AssetSearchBuild
|
||||||
qb.where((eb) => eb.not(eb.exists((eb) => eb.selectFrom('album_asset').whereRef('assetId', '=', 'asset.id')))),
|
qb.where((eb) => eb.not(eb.exists((eb) => eb.selectFrom('album_asset').whereRef('assetId', '=', 'asset.id')))),
|
||||||
)
|
)
|
||||||
.$if(!!options.withExif, withExifInner)
|
.$if(!!options.withExif, withExifInner)
|
||||||
.$if(!!(options.withFaces || options.withPeople || options.personIds), (qb) => qb.select(withFacesAndPeople))
|
.$if(!!(options.withFaces || options.withPeople), (qb) => qb.select(withFacesAndPeople))
|
||||||
.$if(!options.withDeleted, (qb) => qb.where('asset.deletedAt', 'is', null));
|
.$if(!options.withDeleted, (qb) => qb.where('asset.deletedAt', 'is', null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Kysely } from 'kysely';
|
import { Kysely } from 'kysely';
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
|
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||||
import { DatabaseRepository } from 'src/repositories/database.repository';
|
import { DatabaseRepository } from 'src/repositories/database.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { PartnerRepository } from 'src/repositories/partner.repository';
|
import { PartnerRepository } from 'src/repositories/partner.repository';
|
||||||
|
|
@ -16,7 +17,14 @@ let defaultDatabase: Kysely<DB>;
|
||||||
const setup = (db?: Kysely<DB>) => {
|
const setup = (db?: Kysely<DB>) => {
|
||||||
return newMediumService(SearchService, {
|
return newMediumService(SearchService, {
|
||||||
database: db || defaultDatabase,
|
database: db || defaultDatabase,
|
||||||
real: [AccessRepository, DatabaseRepository, SearchRepository, PartnerRepository, PersonRepository],
|
real: [
|
||||||
|
AccessRepository,
|
||||||
|
AssetRepository,
|
||||||
|
DatabaseRepository,
|
||||||
|
SearchRepository,
|
||||||
|
PartnerRepository,
|
||||||
|
PersonRepository,
|
||||||
|
],
|
||||||
mock: [LoggingRepository],
|
mock: [LoggingRepository],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -52,4 +60,32 @@ describe(SearchService.name, () => {
|
||||||
expect.objectContaining({ id: assets[1].id }),
|
expect.objectContaining({ id: assets[1].id }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('searchStatistics', () => {
|
||||||
|
it('should return statistics when filtering by personIds', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
const { person } = await ctx.newPerson({ ownerId: user.id });
|
||||||
|
await ctx.newAssetFace({ assetId: asset.id, personId: person.id });
|
||||||
|
|
||||||
|
const auth = factory.auth({ user: { id: user.id } });
|
||||||
|
|
||||||
|
const result = await sut.searchStatistics(auth, { personIds: [person.id] });
|
||||||
|
|
||||||
|
expect(result).toEqual({ total: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return zero when no assets match the personIds filter', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { person } = await ctx.newPerson({ ownerId: user.id });
|
||||||
|
|
||||||
|
const auth = factory.auth({ user: { id: user.id } });
|
||||||
|
|
||||||
|
const result = await sut.searchStatistics(auth, { personIds: [person.id] });
|
||||||
|
|
||||||
|
expect(result).toEqual({ total: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue