diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 6a7ec55130..a8fb56fa65 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -20,7 +20,7 @@ import { withExifInner, withSearchOrder, } from 'src/utils/database'; -import { paginationHelper } from 'src/utils/pagination'; +import { paginationHelper, PaginationOptions } from 'src/utils/pagination'; import z from 'zod'; export interface SearchAssetIdOptions { @@ -149,11 +149,6 @@ export interface AssetSearchBuilderV3Options { order?: SearchOrder; } -export interface AssetSearchPaginationV3Options { - size: number; - offset?: number; -} - export type SmartSearchOptions = SearchDateOptions & SearchEmbeddingOptions & SearchExifOptions & @@ -532,16 +527,16 @@ export class SearchRepository { // TODO(v4): drop the V3 suffix once the legacy methods are removed @GenerateSql(...searchMetadataV3Examples) async searchMetadataV3( - pagination: AssetSearchPaginationV3Options, + pagination: PaginationOptions, options: AssetSearchBuilderV3Options, scope: AssetSearchScope, ) { const items = await withSearchOrder(searchAssetBuilder(this.db, options, scope), options.order) .select(columns.searchAsset) - .limit(pagination.size + 1) - .offset(pagination.offset ?? 0) + .limit(pagination.take + 1) + .offset(pagination.skip ?? 0) .execute(); - return paginationHelper(items, pagination.size); + return paginationHelper(items, pagination.take); } // TODO(v4): drop the V3 suffix once the legacy methods are removed @@ -561,7 +556,7 @@ export class SearchRepository { // TODO(v4): drop the V3 suffix once the legacy methods are removed @GenerateSql(...searchSmartV3Examples) searchSmartV3( - pagination: AssetSearchPaginationV3Options, + pagination: PaginationOptions, options: Omit & { embedding: string }, scope: AssetSearchScope, ) { @@ -572,10 +567,10 @@ export class SearchRepository { .innerJoin('smart_search', 'asset.id', 'smart_search.assetId') .orderBy(sql`smart_search.embedding <=> ${options.embedding}`) .orderBy('asset.id', 'asc') - .limit(pagination.size + 1) - .offset(pagination.offset ?? 0) + .limit(pagination.take + 1) + .offset(pagination.skip ?? 0) .execute(); - return paginationHelper(items, pagination.size); + return paginationHelper(items, pagination.take); }); } diff --git a/server/src/services/search.service.spec.ts b/server/src/services/search.service.spec.ts index a39357cedd..12bb8c9db9 100644 --- a/server/src/services/search.service.spec.ts +++ b/server/src/services/search.service.spec.ts @@ -244,7 +244,7 @@ describe(SearchService.name, () => { mocks.machineLearning.encodeText.mockResolvedValue('[1, 2, 3]'); await sut.searchSmart(auth, { size: 100, filter: {}, query: 'test' }); expect(mocks.search.searchSmartV3).toHaveBeenCalledWith( - { size: 100 }, + { take: 100 }, expect.objectContaining({ embedding: '[1, 2, 3]' }), expect.objectContaining({ lockedOwnerId: expect.any(String) }), ); diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 7db9f31da9..d96050798f 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -240,7 +240,7 @@ export class SearchService extends BaseService { const { offset } = decodeSearchCursor(dto.cursor); const size = dto.size; const { hasNextPage, items } = await this.searchRepository.searchMetadataV3( - { size, offset }, + { take: size, skip: offset }, { filter, withExif: dto.withExif, @@ -287,7 +287,7 @@ export class SearchService extends BaseService { // no cursor until a rank-aware pagination strategy for smart search is decided const { items } = await this.searchRepository.searchSmartV3( - { size: dto.size }, + { take: dto.size }, { filter, withExif: dto.withExif, embedding }, scope, ); diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index eb3dbb1106..5a98f5fffc 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -817,12 +817,12 @@ export function withSearchOrder(qb: ReturnType, order const scopeExample: AssetSearchScope = { userIds: [DummyValue.UUID], lockedOwnerId: DummyValue.UUID }; export const searchMetadataV3Examples: GenerateSqlQueries[] = [ - { name: 'baseline', params: [{ size: 100 }, {}, scopeExample] }, - { name: 'empty', params: [{ size: 100 }, {}, { lockedOwnerId: DummyValue.UUID }] }, + { name: 'baseline', params: [{ take: 100 }, {}, scopeExample] }, + { name: 'empty', params: [{ take: 100 }, {}, { lockedOwnerId: DummyValue.UUID }] }, { name: 'or-exif-only', params: [ - { size: 100 }, + { take: 100 }, { filter: { or: [{ city: { eq: DummyValue.STRING } }] }, }, @@ -831,12 +831,12 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ }, { name: 'string-eq-null', - params: [{ size: 100 }, { filter: { city: { eq: null } } }, scopeExample], + params: [{ take: 100 }, { filter: { city: { eq: null } } }, scopeExample], }, { name: 'string-pattern-like', params: [ - { size: 100 }, + { take: 100 }, { filter: { description: { like: DummyValue.STRING } }, }, @@ -846,7 +846,7 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ { name: 'string-pattern-notLike', params: [ - { size: 100 }, + { take: 100 }, { filter: { description: { notLike: DummyValue.STRING } }, }, @@ -856,7 +856,7 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ { name: 'string-pattern-startsWith', params: [ - { size: 100 }, + { take: 100 }, { filter: { originalFileName: { startsWith: DummyValue.STRING } }, }, @@ -865,16 +865,16 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ }, { name: 'string-similarity-ocr', - params: [{ size: 100 }, { filter: { ocr: { matches: DummyValue.STRING } } }, scopeExample], + params: [{ take: 100 }, { filter: { ocr: { matches: DummyValue.STRING } } }, scopeExample], }, { name: 'ids-any', - params: [{ size: 100 }, { filter: { albumIds: { any: [DummyValue.UUID] } } }, scopeExample], + params: [{ take: 100 }, { filter: { albumIds: { any: [DummyValue.UUID] } } }, scopeExample], }, { name: 'ids-all', params: [ - { size: 100 }, + { take: 100 }, { filter: { personIds: { all: [DummyValue.UUID, DummyValue.UUID_1] } }, }, @@ -883,16 +883,16 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ }, { name: 'ids-all-single', - params: [{ size: 100 }, { filter: { albumIds: { all: [DummyValue.UUID] } } }, scopeExample], + params: [{ take: 100 }, { filter: { albumIds: { all: [DummyValue.UUID] } } }, scopeExample], }, { name: 'ids-none', - params: [{ size: 100 }, { filter: { tagIds: { none: [DummyValue.UUID] } } }, scopeExample], + params: [{ take: 100 }, { filter: { tagIds: { none: [DummyValue.UUID] } } }, scopeExample], }, { name: 'ids-tags-all', params: [ - { size: 100 }, + { take: 100 }, { filter: { tagIds: { all: [DummyValue.UUID, DummyValue.UUID_1] } }, }, @@ -901,16 +901,16 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ }, { name: 'has-albums-false', - params: [{ size: 100 }, { filter: { hasAlbums: { eq: false } } }, scopeExample], + params: [{ take: 100 }, { filter: { hasAlbums: { eq: false } } }, scopeExample], }, { name: 'is-encoded', - params: [{ size: 100 }, { filter: { isEncoded: { eq: true } } }, scopeExample], + params: [{ take: 100 }, { filter: { isEncoded: { eq: true } } }, scopeExample], }, { name: 'number-range', params: [ - { size: 100 }, + { take: 100 }, { filter: { fileSizeInBytes: { gte: 100, lte: 1000 } }, }, @@ -919,12 +919,12 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ }, { name: 'date-eq', - params: [{ size: 100 }, { filter: { takenAt: { eq: DummyValue.DATE } } }, scopeExample], + params: [{ take: 100 }, { filter: { takenAt: { eq: DummyValue.DATE } } }, scopeExample], }, { name: 'date-range', params: [ - { size: 100 }, + { take: 100 }, { filter: { takenAt: { gte: DummyValue.DATE, lt: DummyValue.DATE } }, }, @@ -934,7 +934,7 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ { name: 'order-fileSize-noExif', params: [ - { size: 100 }, + { take: 100 }, { order: { field: SearchOrderField.FileSizeInBytes, direction: AssetOrder.Desc }, withExif: false, @@ -945,7 +945,7 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ { name: 'order-rating-withExif', params: [ - { size: 100 }, + { take: 100 }, { order: { field: SearchOrderField.Rating, direction: AssetOrder.Asc }, withExif: true, @@ -956,7 +956,7 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ { name: 'or-branches', params: [ - { size: 100 }, + { take: 100 }, { filter: { or: [{ isFavorite: { eq: true } }, { personIds: { any: [DummyValue.UUID] } }], @@ -968,7 +968,7 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ { name: 'or-with-top-level', params: [ - { size: 100 }, + { take: 100 }, { filter: { takenAt: { gte: DummyValue.DATE, lt: DummyValue.DATE }, @@ -980,7 +980,7 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [ }, { name: 'cursor-offset', - params: [{ size: 100, offset: 100 }, { filter: { isFavorite: { eq: true } } }, scopeExample], + params: [{ take: 100, skip: 100 }, { filter: { isFavorite: { eq: true } } }, scopeExample], }, ]; @@ -995,12 +995,12 @@ export const searchRandomV3Examples: GenerateSqlQueries[] = [ export const searchSmartV3Examples: GenerateSqlQueries[] = [ { name: 'baseline', - params: [{ size: 100 }, { embedding: DummyValue.VECTOR }, scopeExample], + params: [{ take: 100 }, { embedding: DummyValue.VECTOR }, scopeExample], }, { name: 'with-filter', params: [ - { size: 100 }, + { take: 100 }, { embedding: DummyValue.VECTOR, filter: { takenAt: { gte: DummyValue.DATE, lt: DummyValue.DATE } }, @@ -1010,7 +1010,7 @@ export const searchSmartV3Examples: GenerateSqlQueries[] = [ }, { name: 'cursor-offset', - params: [{ size: 100, offset: 100 }, { embedding: DummyValue.VECTOR }, scopeExample], + params: [{ take: 100, skip: 100 }, { embedding: DummyValue.VECTOR }, scopeExample], }, ]; diff --git a/server/test/medium/specs/services/search.service.spec.ts b/server/test/medium/specs/services/search.service.spec.ts index 668ee58515..3a64bf53df 100644 --- a/server/test/medium/specs/services/search.service.spec.ts +++ b/server/test/medium/specs/services/search.service.spec.ts @@ -406,12 +406,12 @@ describe(SearchService.name, () => { const options = { filter: {}, embedding: unitVector(0) }; const scope = { userIds: [user.id], lockedOwnerId: user.id }; - const firstPage = await searchRepository.searchSmartV3({ size: 2 }, options, scope); + const firstPage = await searchRepository.searchSmartV3({ take: 2 }, options, scope); expect(firstPage.items.length).toBe(2); expect(firstPage.items[0].id).toBe(assetIds[0]); expect(firstPage.hasNextPage).toBe(true); - const secondPage = await searchRepository.searchSmartV3({ size: 2, offset: 2 }, options, scope); + const secondPage = await searchRepository.searchSmartV3({ take: 2, skip: 2 }, options, scope); expect(secondPage.items.length).toBe(1); expect(secondPage.hasNextPage).toBe(false); });