diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index 367c39dae9..c51ad8e06a 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -46,7 +46,7 @@ export class SearchController { @Get('explore') @Authenticated() getExploreData(@Auth() auth: AuthDto): Promise { - return this.service.getExploreData(auth) as Promise; + return this.service.getExploreData(auth); } @Get('person') diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index d1c08b90bc..89062c210a 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -7,7 +7,6 @@ import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum'; -import { SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository'; import { anyUuid, asUuid, @@ -687,10 +686,7 @@ export class AssetRepository { } @GenerateSql({ params: [DummyValue.UUID, { minAssetsPerField: 5, maxFields: 12 }] }) - async getAssetIdByCity( - ownerId: string, - { minAssetsPerField, maxFields }: AssetExploreFieldOptions, - ): Promise> { + async getAssetIdByCity(ownerId: string, { minAssetsPerField, maxFields }: AssetExploreFieldOptions) { const items = await this.db .with('cities', (qb) => qb @@ -705,6 +701,7 @@ export class AssetRepository { .innerJoin('cities', 'exif.city', 'cities.city') .distinctOn('exif.city') .select(['assetId as data', 'exif.city as value']) + .$narrowType<{ value: NotNull }>() .where('ownerId', '=', asUuid(ownerId)) .where('isVisible', '=', true) .where('isArchived', '=', false) @@ -713,7 +710,7 @@ export class AssetRepository { .limit(maxFields) .execute(); - return { fieldName: 'exifInfo.city', items: items as SearchExploreItemSet }; + return { fieldName: 'exifInfo.city', items }; } @GenerateSql({ diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index d55d863ea7..0383a54a27 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -6,7 +6,7 @@ import { AssetFaces, DB, FaceSearch, Person } from 'src/db'; import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { AssetFileType, SourceType } from 'src/enum'; import { removeUndefinedKeys } from 'src/utils/database'; -import { PaginationOptions } from 'src/utils/pagination'; +import { paginationHelper, PaginationOptions } from 'src/utils/pagination'; export interface PersonSearchOptions { minimumFaceCount: number; @@ -200,11 +200,7 @@ export class PersonRepository { .limit(pagination.take + 1) .execute(); - if (items.length > pagination.take) { - return { items: items.slice(0, -1), hasNextPage: true }; - } - - return { items, hasNextPage: false }; + return paginationHelper(items, pagination.take); } @GenerateSql() diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 0c958fec02..b991ecc78b 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -8,41 +8,10 @@ import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetStatus, AssetType } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { anyUuid, asUuid, searchAssetBuilder, vectorIndexQuery } from 'src/utils/database'; +import { paginationHelper } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; -export interface SearchResult { - /** total matches */ - total: number; - /** collection size */ - count: number; - /** current page */ - page: number; - /** items for page */ - items: T[]; - /** score */ - distances: number[]; - facets: SearchFacet[]; -} - -export interface SearchFacet { - fieldName: string; - counts: Array<{ - count: number; - value: string; - }>; -} - -export type SearchExploreItemSet = Array<{ - value: string; - data: T; -}>; - -export interface SearchExploreItem { - fieldName: string; - items: SearchExploreItemSet; -} - -export interface SearchAssetIDOptions { +export interface SearchAssetIdOptions { checksum?: Buffer; deviceAssetId?: string; id?: string; @@ -54,7 +23,7 @@ export interface SearchUserIdOptions { userIds?: string[]; } -export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions; +export type SearchIdOptions = SearchAssetIdOptions & SearchUserIdOptions; export interface SearchStatusOptions { isArchived?: boolean; @@ -144,8 +113,6 @@ type BaseAssetSearchOptions = SearchDateOptions & export type AssetSearchOptions = BaseAssetSearchOptions & SearchRelationOptions; -export type AssetSearchOneToOneRelationOptions = BaseAssetSearchOptions & SearchOneToOneRelationOptions; - export type AssetSearchBuilderOptions = Omit; export type SmartSearchOptions = SearchDateOptions & @@ -226,9 +193,8 @@ export class SearchRepository { .limit(pagination.size + 1) .offset((pagination.page - 1) * pagination.size) .execute(); - const hasNextPage = items.length > pagination.size; - items.splice(pagination.size); - return { items, hasNextPage }; + + return paginationHelper(items, pagination.size); } @GenerateSql({ @@ -283,9 +249,7 @@ export class SearchRepository { .offset((pagination.page - 1) * pagination.size) .execute(); - const hasNextPage = items.length > pagination.size; - items.splice(pagination.size); - return { items, hasNextPage }; + return paginationHelper(items, pagination.size); } @GenerateSql({ diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 442d49136c..df286d1809 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -15,7 +15,6 @@ import { SmartSearchDto, } from 'src/dtos/search.dto'; import { AssetOrder } from 'src/enum'; -import { SearchExploreItem } from 'src/repositories/search.repository'; import { BaseService } from 'src/services/base.service'; import { getMyPartnerIds } from 'src/utils/asset.util'; import { isSmartSearchEnabled } from 'src/utils/misc'; @@ -32,7 +31,7 @@ export class SearchService extends BaseService { return places.map((place) => mapPlaces(place)); } - async getExploreData(auth: AuthDto): Promise[]> { + async getExploreData(auth: AuthDto) { const options = { maxFields: 12, minAssetsPerField: 5 }; const cities = await this.assetRepository.getAssetIdByCity(auth.user.id, options); const assets = await this.assetRepository.getByIdsWithAllRelationsButStacks(cities.items.map(({ data }) => data)); diff --git a/server/src/utils/pagination.ts b/server/src/utils/pagination.ts index eb4106c86a..e440638a72 100644 --- a/server/src/utils/pagination.ts +++ b/server/src/utils/pagination.ts @@ -8,22 +8,6 @@ export interface PaginationResult { hasNextPage: boolean; } -export type Paginated = Promise>; - -/** @deprecated use `this.db. ... .stream()` instead */ -export async function* usePagination( - pageSize: number, - getNextPage: (pagination: PaginationOptions) => PaginationResult | Paginated, -) { - let hasNextPage = true; - - for (let skip = 0; hasNextPage; skip += pageSize) { - const result = await getNextPage({ take: pageSize, skip }); - hasNextPage = result.hasNextPage; - yield result.items; - } -} - export function paginationHelper(items: Entity[], take: number): PaginationResult { const hasNextPage = items.length > take; items.splice(take);