diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts index a2b4121cebad0..79e04bae320cd 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts @@ -235,6 +235,81 @@ describe('createPointInTimeFinder()', () => { ); }); + test('does not yield empty first page', async () => { + repository.openPointInTimeForType.mockResolvedValueOnce({ + id: 'abc123', + }); + repository.find.mockResolvedValueOnce({ + total: 2, + saved_objects: [], + pit_id: 'abc123', + per_page: 2, + page: 0, + }); + + const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { + type: ['visualization'], + search: 'foo*', + }; + + const internalOptions = {}; + const finder = new PointInTimeFinder(findOptions, { + logger, + client: repository, + internalOptions, + }); + + const hits: SavedObjectsFindResult[] = []; + let pageCount = 0; + for await (const result of finder.find()) { + hits.push(...result.saved_objects); + pageCount++; + } + + expect(pageCount).toEqual(0); + expect(hits.length).toEqual(0); + }); + + test('yields empty first page if aggregations are used', async () => { + repository.openPointInTimeForType.mockResolvedValueOnce({ + id: 'abc123', + }); + repository.find.mockResolvedValueOnce({ + total: 2, + saved_objects: [], + pit_id: 'abc123', + per_page: 2, + page: 0, + }); + + const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { + type: ['visualization'], + search: 'foo*', + aggs: { + some: { + avg: { field: 'fo' }, + }, + }, + }; + + const internalOptions = {}; + const finder = new PointInTimeFinder(findOptions, { + logger, + client: repository, + internalOptions, + }); + + const hits: SavedObjectsFindResult[] = []; + let pageCount = 0; + for await (const result of finder.find()) { + hits.push(...result.saved_objects); + pageCount++; + } + + expect(pageCount).toEqual(1); + expect(hits.length).toEqual(0); + }); + test('still applies the defaults in the mandatory fields even when `undefined` is explicitly provided', async () => { repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'abc123', diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts index 524b571894007..7a2c124a0e86e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts @@ -93,7 +93,12 @@ export class PointInTimeFinder await this.close(); } - yield results; + // do not yield first page if empty, unless there are aggregations + // (in which case we always want to return at least one page) + if (lastResultsCount > 0 || this.#findOptions.aggs) { + yield results; + } + // We've reached the end when there are fewer hits than our perPage size, // or when `close()` has been called. } while (this.#open && lastResultsCount >= this.#findOptions.perPage!);