From 34c25b43fcfe3e33af4e03e1ebd870757d6a806c Mon Sep 17 00:00:00 2001 From: timonrieger Date: Sat, 25 Jul 2026 18:33:45 +0200 Subject: [PATCH] no smart search cursor --- .../openapi/lib/model/smart_search_dto.dart | 19 +------------------ open-api/immich-openapi-specs.json | 11 ----------- packages/sdk/src/fetch-client.ts | 2 -- server/src/dtos/search.dto.ts | 1 - server/src/services/search.service.spec.ts | 2 +- server/src/services/search.service.ts | 9 ++++----- 6 files changed, 6 insertions(+), 38 deletions(-) diff --git a/mobile/openapi/lib/model/smart_search_dto.dart b/mobile/openapi/lib/model/smart_search_dto.dart index b97b09b7c5..eb1d2f8bad 100644 --- a/mobile/openapi/lib/model/smart_search_dto.dart +++ b/mobile/openapi/lib/model/smart_search_dto.dart @@ -18,7 +18,6 @@ class SmartSearchDto { this.country = const Optional.absent(), this.createdAfter = const Optional.absent(), this.createdBefore = const Optional.absent(), - this.cursor = const Optional.absent(), this.filter = const Optional.absent(), this.isEncoded = const Optional.absent(), this.isFavorite = const Optional.absent(), @@ -78,15 +77,6 @@ class SmartSearchDto { /// Optional createdBefore; - /// Cursor for the next page of results - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - Optional cursor; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -316,7 +306,6 @@ class SmartSearchDto { other.country == country && other.createdAfter == createdAfter && other.createdBefore == createdBefore && - other.cursor == cursor && other.filter == filter && other.isEncoded == isEncoded && other.isFavorite == isFavorite && @@ -356,7 +345,6 @@ class SmartSearchDto { (country == null ? 0 : country!.hashCode) + (createdAfter == null ? 0 : createdAfter!.hashCode) + (createdBefore == null ? 0 : createdBefore!.hashCode) + - (cursor == null ? 0 : cursor!.hashCode) + (filter == null ? 0 : filter!.hashCode) + (isEncoded == null ? 0 : isEncoded!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) + @@ -389,7 +377,7 @@ class SmartSearchDto { (withExif == null ? 0 : withExif!.hashCode); @override - String toString() => 'SmartSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, cursor=$cursor, filter=$filter, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, page=$page, personIds=$personIds, query=$query, queryAssetId=$queryAssetId, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif]'; + String toString() => 'SmartSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, filter=$filter, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, page=$page, personIds=$personIds, query=$query, queryAssetId=$queryAssetId, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif]'; Map toJson() { final json = {}; @@ -417,10 +405,6 @@ class SmartSearchDto { ? value.millisecondsSinceEpoch : value.toUtc().toIso8601String()); } - if (this.cursor.isPresent) { - final value = this.cursor.value; - json[r'cursor'] = value; - } if (this.filter.isPresent) { final value = this.filter.value; json[r'filter'] = value; @@ -572,7 +556,6 @@ class SmartSearchDto { country: json.containsKey(r'country') ? Optional.present(mapValueOfType(json, r'country')) : const Optional.absent(), createdAfter: json.containsKey(r'createdAfter') ? Optional.present(mapDateTime(json, r'createdAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(), createdBefore: json.containsKey(r'createdBefore') ? Optional.present(mapDateTime(json, r'createdBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(), - cursor: json.containsKey(r'cursor') ? Optional.present(mapValueOfType(json, r'cursor')) : const Optional.absent(), filter: json.containsKey(r'filter') ? Optional.present(SearchFilter.fromJson(json[r'filter'])) : const Optional.absent(), isEncoded: json.containsKey(r'isEncoded') ? Optional.present(mapValueOfType(json, r'isEncoded')) : const Optional.absent(), isFavorite: json.containsKey(r'isFavorite') ? Optional.present(mapValueOfType(json, r'isFavorite')) : const Optional.absent(), diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index e68f85bc40..269b09d75c 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -25456,17 +25456,6 @@ ], "x-immich-state": "Deprecated" }, - "cursor": { - "description": "Cursor for the next page of results", - "minLength": 1, - "type": "string", - "x-immich-history": [ - { - "version": "v3.1.0", - "state": "Added" - } - ] - }, "filter": { "$ref": "#/components/schemas/SearchFilter", "x-immich-history": [ diff --git a/packages/sdk/src/fetch-client.ts b/packages/sdk/src/fetch-client.ts index ff1a7104ce..8fd1da659c 100644 --- a/packages/sdk/src/fetch-client.ts +++ b/packages/sdk/src/fetch-client.ts @@ -1990,8 +1990,6 @@ export type SmartSearchDto = { createdAfter?: string; /** Filter by creation date (before) */ createdBefore?: string; - /** Cursor for the next page of results */ - cursor?: string; filter?: SearchFilter; /** Filter by encoded status */ isEncoded?: boolean; diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index 6c2fdf22ad..b1f2422468 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -387,7 +387,6 @@ const SmartSearchSchema = withShapeExclusivity( language: z.string().optional().describe('Search language code'), page: z.int().min(1).optional().describe('Page number').meta(DEPRECATED_FLAT_FIELD), filter: filterField, - cursor: cursorField, }).strict(), ).meta({ id: 'SmartSearchDto' }); diff --git a/server/src/services/search.service.spec.ts b/server/src/services/search.service.spec.ts index 297e8a36e9..a39357cedd 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, offset: 0 }, + { size: 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 f9465e0206..7db9f31da9 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -285,15 +285,14 @@ export class SearchService extends BaseService { this.resolveEmbedding(auth, dto, machineLearning), ]); - const { offset } = decodeSearchCursor(dto.cursor); - const size = dto.size; - const { hasNextPage, items } = await this.searchRepository.searchSmartV3( - { size, offset }, + // no cursor until a rank-aware pagination strategy for smart search is decided + const { items } = await this.searchRepository.searchSmartV3( + { size: dto.size }, { filter, withExif: dto.withExif, embedding }, scope, ); - return this.mapResponse(items, { auth }, { nextCursor: hasNextPage ? encodeSearchCursor(offset + size) : null }); + return this.mapResponse(items, { auth }); } private async resolveSearchScopeV3(