feat: Search filtering logic (#6968)

* commit

* controller/service/repository logic

* use enum

* openapi

* suggest people

* suggest place/camera

* cursor hover

* refactor

* Add try catch

* Remove get people with name service

* Remove deadcode

* people selection

* People placement

* sort people

* Update server/src/domain/repositories/metadata.repository.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* pr feedback

* styling

* done

* open api

* fix test

* use string type

* remmove bad merge

* use correct type

* fix test

* fix lint

* remove unused code

* remove unused code

* pr feedback

* pr feedback

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
Alex
2024-02-13 13:54:58 -06:00
committed by GitHub
parent 0c45f51a29
commit 4b3f8d1946
24 changed files with 1145 additions and 118 deletions

View File

@@ -39,4 +39,9 @@ export interface IMetadataRepository {
readTags(path: string): Promise<ImmichTags | null>;
writeTags(path: string, tags: Partial<Tags>): Promise<void>;
extractBinaryTag(tagName: string, path: string): Promise<Buffer>;
getCountries(userId: string): Promise<string[]>;
getStates(userId: string, country?: string): Promise<string[]>;
getCities(userId: string, country?: string, state?: string): Promise<string[]>;
getCameraMakes(userId: string, model?: string): Promise<string[]>;
getCameraModels(userId: string, make?: string): Promise<string[]>;
}

View File

@@ -0,0 +1,33 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
export enum SearchSuggestionType {
COUNTRY = 'country',
STATE = 'state',
CITY = 'city',
CAMERA_MAKE = 'camera-make',
CAMERA_MODEL = 'camera-model',
}
export class SearchSuggestionRequestDto {
@IsEnum(SearchSuggestionType)
@IsNotEmpty()
@ApiProperty({ enumName: 'SearchSuggestionType', enum: SearchSuggestionType })
type!: SearchSuggestionType;
@IsString()
@IsOptional()
country?: string;
@IsString()
@IsOptional()
state?: string;
@IsString()
@IsOptional()
make?: string;
@IsString()
@IsOptional()
model?: string;
}

View File

@@ -4,6 +4,7 @@ import {
authStub,
newAssetRepositoryMock,
newMachineLearningRepositoryMock,
newMetadataRepositoryMock,
newPartnerRepositoryMock,
newPersonRepositoryMock,
newSearchRepositoryMock,
@@ -14,6 +15,7 @@ import { mapAsset } from '../asset';
import {
IAssetRepository,
IMachineLearningRepository,
IMetadataRepository,
IPartnerRepository,
IPersonRepository,
ISearchRepository,
@@ -32,6 +34,7 @@ describe(SearchService.name, () => {
let personMock: jest.Mocked<IPersonRepository>;
let searchMock: jest.Mocked<ISearchRepository>;
let partnerMock: jest.Mocked<IPartnerRepository>;
let metadataMock: jest.Mocked<IMetadataRepository>;
beforeEach(() => {
assetMock = newAssetRepositoryMock();
@@ -40,7 +43,9 @@ describe(SearchService.name, () => {
personMock = newPersonRepositoryMock();
searchMock = newSearchRepositoryMock();
partnerMock = newPartnerRepositoryMock();
sut = new SearchService(configMock, machineMock, personMock, searchMock, assetMock, partnerMock);
metadataMock = newMetadataRepositoryMock();
sut = new SearchService(configMock, machineMock, personMock, searchMock, assetMock, partnerMock, metadataMock);
});
it('should work', () => {

View File

@@ -7,6 +7,7 @@ import { PersonResponseDto } from '../person';
import {
IAssetRepository,
IMachineLearningRepository,
IMetadataRepository,
IPartnerRepository,
IPersonRepository,
ISearchRepository,
@@ -16,6 +17,7 @@ import {
} from '../repositories';
import { FeatureFlag, SystemConfigCore } from '../system-config';
import { MetadataSearchDto, SearchDto, SearchPeopleDto, SmartSearchDto } from './dto';
import { SearchSuggestionRequestDto, SearchSuggestionType } from './dto/search-suggestion.dto';
import { SearchResponseDto } from './response-dto';
@Injectable()
@@ -30,6 +32,7 @@ export class SearchService {
@Inject(ISearchRepository) private searchRepository: ISearchRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
@Inject(IMetadataRepository) private metadataRepository: IMetadataRepository,
) {
this.configCore = SystemConfigCore.create(configRepository);
}
@@ -176,4 +179,28 @@ export class SearchService {
},
};
}
async getSearchSuggestions(auth: AuthDto, dto: SearchSuggestionRequestDto): Promise<string[]> {
if (dto.type === SearchSuggestionType.COUNTRY) {
return this.metadataRepository.getCountries(auth.user.id);
}
if (dto.type === SearchSuggestionType.STATE) {
return this.metadataRepository.getStates(auth.user.id, dto.country);
}
if (dto.type === SearchSuggestionType.CITY) {
return this.metadataRepository.getCities(auth.user.id, dto.country, dto.state);
}
if (dto.type === SearchSuggestionType.CAMERA_MAKE) {
return this.metadataRepository.getCameraMakes(auth.user.id, dto.model);
}
if (dto.type === SearchSuggestionType.CAMERA_MODEL) {
return this.metadataRepository.getCameraModels(auth.user.id, dto.make);
}
return [];
}
}