1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

feat: extend smart search, match exif-description too

This commit is contained in:
TheHamkerCat 2024-11-28 02:42:21 +05:30
parent 3d61548d7d
commit cacf818ce5
No known key found for this signature in database
GPG key ID: 4B32A124C668ECFF
2 changed files with 41 additions and 7 deletions

View file

@ -104,6 +104,10 @@ export interface SearchExifOptions {
state?: string | null; state?: string | null;
} }
export interface SearchTextOptions {
query?: string;
}
export interface SearchEmbeddingOptions { export interface SearchEmbeddingOptions {
embedding: number[]; embedding: number[];
userIds: string[]; userIds: string[];
@ -140,6 +144,7 @@ export type AssetSearchBuilderOptions = Omit<AssetSearchOptions, 'orderDirection
export type SmartSearchOptions = SearchDateOptions & export type SmartSearchOptions = SearchDateOptions &
SearchEmbeddingOptions & SearchEmbeddingOptions &
SearchExifOptions & SearchExifOptions &
SearchTextOptions &
SearchOneToOneRelationOptions & SearchOneToOneRelationOptions &
SearchStatusOptions & SearchStatusOptions &
SearchUserIdOptions & SearchUserIdOptions &

View file

@ -115,6 +115,7 @@ export class SearchRepository implements ISearchRepository {
{ {
takenAfter: DummyValue.DATE, takenAfter: DummyValue.DATE,
embedding: Array.from({ length: 512 }, Math.random), embedding: Array.from({ length: 512 }, Math.random),
query: "beach vacation",
lensModel: DummyValue.STRING, lensModel: DummyValue.STRING,
withStacked: true, withStacked: true,
isFavorite: true, isFavorite: true,
@ -124,23 +125,51 @@ export class SearchRepository implements ISearchRepository {
}) })
async searchSmart( async searchSmart(
pagination: SearchPaginationOptions, pagination: SearchPaginationOptions,
{ embedding, userIds, ...options }: SmartSearchOptions, { embedding, query, userIds, ...options }: SmartSearchOptions,
): Paginated<AssetEntity> { ): Paginated<AssetEntity> {
let results: PaginationResult<AssetEntity> = { items: [], hasNextPage: false }; let results: PaginationResult<AssetEntity> = { items: [], hasNextPage: false };
await this.assetRepository.manager.transaction(async (manager) => { await this.assetRepository.manager.transaction(async (manager) => {
let builder = manager.createQueryBuilder(AssetEntity, 'asset'); let builder = manager.createQueryBuilder(AssetEntity, 'asset')
.leftJoinAndSelect('asset.exifInfo', 'exif');
builder = searchAssetBuilder(builder, options); builder = searchAssetBuilder(builder, options);
builder builder.andWhere('asset.ownerId IN (:...userIds)');
.innerJoin('asset.smartSearch', 'search')
.andWhere('asset.ownerId IN (:...userIds )') const parameters: Record<string, any> = { userIds };
.orderBy('search.embedding <=> :embedding')
.setParameters({ userIds, embedding: asVector(embedding) }); if (embedding) {
builder
.innerJoin('asset.smartSearch', 'search')
.addSelect('search.embedding <=> :embedding', 'similarity');
parameters.embedding = asVector(embedding);
}
if (query) {
parameters.query = query;
}
if (query && embedding) {
builder.orderBy(`
CASE WHEN to_tsvector('english', COALESCE(exif.description, '')) @@ plainto_tsquery('english', :query)
THEN
ts_rank(
to_tsvector('english', COALESCE(exif.description, '')),
plainto_tsquery('english', :query)
)
ELSE 0 END * 0.7 +
COALESCE((1 - (search.embedding <=> :embedding)), 0) * 0.3
`, 'DESC');
} else if (embedding) {
builder.orderBy('search.embedding <=> :embedding', 'ASC');
}
builder.setParameters(parameters);
const runtimeConfig = this.getRuntimeConfig(pagination.size); const runtimeConfig = this.getRuntimeConfig(pagination.size);
if (runtimeConfig) { if (runtimeConfig) {
await manager.query(runtimeConfig); await manager.query(runtimeConfig);
} }
results = await paginatedBuilder<AssetEntity>(builder, { results = await paginatedBuilder<AssetEntity>(builder, {
mode: PaginationMode.LIMIT_OFFSET, mode: PaginationMode.LIMIT_OFFSET,
skip: (pagination.page - 1) * pagination.size, skip: (pagination.page - 1) * pagination.size,