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<SearchExploreResponseDto[]> {
-    return this.service.getExploreData(auth) as Promise<SearchExploreResponseDto[]>;
+    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<SearchExploreItem<string>> {
+  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<string> };
+    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<T> {
-  /** 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<T> = Array<{
-  value: string;
-  data: T;
-}>;
-
-export interface SearchExploreItem<T> {
-  fieldName: string;
-  items: SearchExploreItemSet<T>;
-}
-
-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<AssetSearchOptions, 'orderDirection'>;
 
 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<SearchExploreItem<AssetResponseDto>[]> {
+  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<T> {
   hasNextPage: boolean;
 }
 
-export type Paginated<T> = Promise<PaginationResult<T>>;
-
-/** @deprecated use `this.db. ... .stream()` instead */
-export async function* usePagination<T>(
-  pageSize: number,
-  getNextPage: (pagination: PaginationOptions) => PaginationResult<T> | Paginated<T>,
-) {
-  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<Entity extends object>(items: Entity[], take: number): PaginationResult<Entity> {
   const hasNextPage = items.length > take;
   items.splice(take);