mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(web): add search filter for camera lens model. (#21792)
This commit is contained in:
parent
d9cddeb0f1
commit
78fb815cdb
12 changed files with 133 additions and 15 deletions
|
|
@ -249,6 +249,7 @@ export enum SearchSuggestionType {
|
|||
CITY = 'city',
|
||||
CAMERA_MAKE = 'camera-make',
|
||||
CAMERA_MODEL = 'camera-model',
|
||||
CAMERA_LENS_MODEL = 'camera-lens-model',
|
||||
}
|
||||
|
||||
export class SearchSuggestionRequestDto {
|
||||
|
|
@ -271,6 +272,10 @@ export class SearchSuggestionRequestDto {
|
|||
@Optional()
|
||||
model?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
lensModel?: string;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
@PropertyLifecycle({ addedAt: 'v111.0.0' })
|
||||
includeNull?: boolean;
|
||||
|
|
|
|||
|
|
@ -290,3 +290,15 @@ where
|
|||
and "visibility" = $2
|
||||
and "deletedAt" is null
|
||||
and "model" is not null
|
||||
|
||||
-- SearchRepository.getCameraLensModels
|
||||
select distinct
|
||||
on ("lensModel") "lensModel"
|
||||
from
|
||||
"asset_exif"
|
||||
inner join "asset" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"ownerId" = any ($1::uuid[])
|
||||
and "visibility" = $2
|
||||
and "deletedAt" is null
|
||||
and "lensModel" is not null
|
||||
|
|
|
|||
|
|
@ -160,10 +160,17 @@ export interface GetCitiesOptions extends GetStatesOptions {
|
|||
|
||||
export interface GetCameraModelsOptions {
|
||||
make?: string;
|
||||
lensModel?: string;
|
||||
}
|
||||
|
||||
export interface GetCameraMakesOptions {
|
||||
model?: string;
|
||||
lensModel?: string;
|
||||
}
|
||||
|
||||
export interface GetCameraLensModelsOptions {
|
||||
make?: string;
|
||||
model?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -457,25 +464,40 @@ export class SearchRepository {
|
|||
return res.map((row) => row.city!);
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID], DummyValue.STRING] })
|
||||
async getCameraMakes(userIds: string[], { model }: GetCameraMakesOptions): Promise<string[]> {
|
||||
@GenerateSql({ params: [[DummyValue.UUID], DummyValue.STRING, DummyValue.STRING] })
|
||||
async getCameraMakes(userIds: string[], { model, lensModel }: GetCameraMakesOptions): Promise<string[]> {
|
||||
const res = await this.getExifField('make', userIds)
|
||||
.$if(!!model, (qb) => qb.where('model', '=', model!))
|
||||
.$if(!!lensModel, (qb) => qb.where('lensModel', '=', lensModel!))
|
||||
.execute();
|
||||
|
||||
return res.map((row) => row.make!);
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID], DummyValue.STRING] })
|
||||
async getCameraModels(userIds: string[], { make }: GetCameraModelsOptions): Promise<string[]> {
|
||||
@GenerateSql({ params: [[DummyValue.UUID], DummyValue.STRING, DummyValue.STRING] })
|
||||
async getCameraModels(userIds: string[], { make, lensModel }: GetCameraModelsOptions): Promise<string[]> {
|
||||
const res = await this.getExifField('model', userIds)
|
||||
.$if(!!make, (qb) => qb.where('make', '=', make!))
|
||||
.$if(!!lensModel, (qb) => qb.where('lensModel', '=', lensModel!))
|
||||
.execute();
|
||||
|
||||
return res.map((row) => row.model!);
|
||||
}
|
||||
|
||||
private getExifField<K extends 'city' | 'state' | 'country' | 'make' | 'model'>(field: K, userIds: string[]) {
|
||||
@GenerateSql({ params: [[DummyValue.UUID], DummyValue.STRING] })
|
||||
async getCameraLensModels(userIds: string[], { make, model }: GetCameraLensModelsOptions): Promise<string[]> {
|
||||
const res = await this.getExifField('lensModel', userIds)
|
||||
.$if(!!make, (qb) => qb.where('make', '=', make!))
|
||||
.$if(!!model, (qb) => qb.where('model', '=', model!))
|
||||
.execute();
|
||||
|
||||
return res.map((row) => row.lensModel!);
|
||||
}
|
||||
|
||||
private getExifField<K extends 'city' | 'state' | 'country' | 'make' | 'model' | 'lensModel'>(
|
||||
field: K,
|
||||
userIds: string[],
|
||||
) {
|
||||
return this.db
|
||||
.selectFrom('asset_exif')
|
||||
.select(field)
|
||||
|
|
|
|||
|
|
@ -179,6 +179,26 @@ describe(SearchService.name, () => {
|
|||
).resolves.toEqual(['Fujifilm X100VI', null]);
|
||||
expect(mocks.search.getCameraModels).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
|
||||
});
|
||||
|
||||
it('should return search suggestions for camera lens model', async () => {
|
||||
mocks.search.getCameraLensModels.mockResolvedValue(['10-24mm']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.CAMERA_LENS_MODEL }),
|
||||
).resolves.toEqual(['10-24mm']);
|
||||
expect(mocks.search.getCameraLensModels).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
|
||||
});
|
||||
|
||||
it('should return search suggestions for camera lens model (including null)', async () => {
|
||||
mocks.search.getCameraLensModels.mockResolvedValue(['10-24mm']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.CAMERA_LENS_MODEL }),
|
||||
).resolves.toEqual(['10-24mm', null]);
|
||||
expect(mocks.search.getCameraLensModels).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchSmart', () => {
|
||||
|
|
|
|||
|
|
@ -177,6 +177,9 @@ export class SearchService extends BaseService {
|
|||
case SearchSuggestionType.CAMERA_MODEL: {
|
||||
return this.searchRepository.getCameraModels(userIds, dto);
|
||||
}
|
||||
case SearchSuggestionType.CAMERA_LENS_MODEL: {
|
||||
return this.searchRepository.getCameraLensModels(userIds, dto);
|
||||
}
|
||||
default: {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue