1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-10 13:56:47 +01:00
immich/server/src/domain/search/search.service.ts

123 lines
4.3 KiB
TypeScript
Raw Normal View History

2023-12-08 17:15:46 +01:00
import { AssetEntity } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { Inject, Injectable } from '@nestjs/common';
import { AssetResponseDto, mapAsset } from '../asset';
import { AuthDto } from '../auth';
2023-12-08 17:15:46 +01:00
import { PersonResponseDto } from '../person';
import {
2023-10-09 16:25:03 +02:00
IAssetRepository,
IMachineLearningRepository,
IPartnerRepository,
2023-10-09 16:25:03 +02:00
IPersonRepository,
2023-12-08 17:15:46 +01:00
ISmartInfoRepository,
2023-10-09 16:25:03 +02:00
ISystemConfigRepository,
SearchExploreItem,
SearchStrategy,
2023-10-09 16:25:03 +02:00
} from '../repositories';
import { FeatureFlag, SystemConfigCore } from '../system-config';
import { SearchDto, SearchPeopleDto } from './dto';
2023-10-09 16:25:03 +02:00
import { SearchResponseDto } from './response-dto';
@Injectable()
export class SearchService {
private logger = new ImmichLogger(SearchService.name);
private configCore: SystemConfigCore;
constructor(
2023-12-08 17:15:46 +01:00
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
@Inject(IMachineLearningRepository) private machineLearning: IMachineLearningRepository,
@Inject(IPersonRepository) private personRepository: IPersonRepository,
2023-12-08 17:15:46 +01:00
@Inject(ISmartInfoRepository) private smartInfoRepository: ISmartInfoRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
) {
this.configCore = SystemConfigCore.create(configRepository);
}
async searchPerson(auth: AuthDto, dto: SearchPeopleDto): Promise<PersonResponseDto[]> {
return this.personRepository.getByName(auth.user.id, dto.name, { withHidden: dto.withHidden });
}
async getExploreData(auth: AuthDto): Promise<SearchExploreItem<AssetResponseDto>[]> {
await this.configCore.requireFeature(FeatureFlag.SEARCH);
2023-12-08 17:15:46 +01:00
const options = { maxFields: 12, minAssetsPerField: 5 };
const results = await Promise.all([
this.assetRepository.getAssetIdByCity(auth.user.id, options),
this.assetRepository.getAssetIdByTag(auth.user.id, options),
2023-12-08 17:15:46 +01:00
]);
const assetIds = new Set<string>(results.flatMap((field) => field.items.map((item) => item.data)));
const assets = await this.assetRepository.getByIds(Array.from(assetIds));
const assetMap = new Map<string, AssetResponseDto>(assets.map((asset) => [asset.id, mapAsset(asset)]));
return results.map(({ fieldName, items }) => ({
fieldName,
2023-12-08 17:15:46 +01:00
items: items.map(({ value, data }) => ({ value, data: assetMap.get(data) as AssetResponseDto })),
}));
}
async search(auth: AuthDto, dto: SearchDto): Promise<SearchResponseDto> {
await this.configCore.requireFeature(FeatureFlag.SEARCH);
const { machineLearning } = await this.configCore.getConfig();
2023-12-08 17:15:46 +01:00
const query = dto.q || dto.query;
if (!query) {
throw new Error('Missing query');
}
let strategy = SearchStrategy.TEXT;
if (dto.smart || dto.clip) {
await this.configCore.requireFeature(FeatureFlag.SMART_SEARCH);
strategy = SearchStrategy.SMART;
2023-12-08 17:15:46 +01:00
}
const userIds = await this.getUserIdsToSearch(auth);
const withArchived = dto.withArchived || false;
2023-12-08 17:15:46 +01:00
let assets: AssetEntity[] = [];
switch (strategy) {
case SearchStrategy.SMART:
2023-12-08 17:15:46 +01:00
const embedding = await this.machineLearning.encodeText(
machineLearning.url,
{ text: query },
machineLearning.clip,
);
assets = await this.smartInfoRepository.searchCLIP({
userIds: userIds,
embedding,
numResults: 100,
withArchived,
});
break;
case SearchStrategy.TEXT:
assets = await this.assetRepository.searchMetadata(query, userIds, { numResults: 250 });
default:
break;
}
return {
2023-12-08 17:15:46 +01:00
albums: {
total: 0,
count: 0,
items: [],
facets: [],
},
assets: {
2023-12-08 17:15:46 +01:00
total: assets.length,
count: assets.length,
items: assets.map((asset) => mapAsset(asset)),
facets: [],
},
};
}
private async getUserIdsToSearch(auth: AuthDto): Promise<string[]> {
const userIds: string[] = [auth.user.id];
const partners = await this.partnerRepository.getAll(auth.user.id);
const partnersIds = partners
.filter((partner) => partner.sharedBy && partner.inTimeline)
.map((partner) => partner.sharedById);
userIds.push(...partnersIds);
return userIds;
}
}