diff --git a/src/SearchRequestAdapter.js b/src/SearchRequestAdapter.js index 0fb2a75..0947ccc 100644 --- a/src/SearchRequestAdapter.js +++ b/src/SearchRequestAdapter.js @@ -644,7 +644,11 @@ export class SearchRequestAdapter { // Add union parameter if configured if (this.configuration.union) { searchRequest.union = this.configuration.union; - commonParams.page = searches[0].page; + ["page", "per_page", "offset", "limit", "limit_hits"].forEach((paramName) => { + if (searches[0][paramName] != null) { + commonParams[paramName] = searches[0][paramName]; + } + }); } return this.typesenseClient.multiSearch.perform(searchRequest, commonParams); diff --git a/test/SearchRequestAdpater.test.js b/test/SearchRequestAdpater.test.js index 6883f59..c5de6c0 100644 --- a/test/SearchRequestAdpater.test.js +++ b/test/SearchRequestAdpater.test.js @@ -442,6 +442,132 @@ describe("SearchRequestAdapter", () => { ); }); + it("promotes per_page to common params for union search", async () => { + const mockTypesenseClient = { + multiSearch: { + perform: jest.fn().mockResolvedValue({ results: [] }), + }, + }; + + const configuration = new Configuration({ + union: true, + additionalSearchParameters: { + query_by: "name", + }, + }); + + const instantsearchRequests = [ + { + indexName: "products", + params: { query: "test", hitsPerPage: 20 }, + }, + { + indexName: "brands", + params: { query: "test", hitsPerPage: 20 }, + }, + ]; + + const subject = new SearchRequestAdapter(instantsearchRequests, mockTypesenseClient, configuration); + await subject.request(); + + expect(mockTypesenseClient.multiSearch.perform).toHaveBeenCalledWith( + { + union: true, + searches: [ + { + collection: "products", + q: "test", + page: 1, + per_page: 20, + query_by: "name", + highlight_full_fields: "name", + }, + { + collection: "brands", + q: "test", + page: 1, + per_page: 20, + query_by: "name", + highlight_full_fields: "name", + }, + ], + }, + { + page: 1, + per_page: 20, + }, + ); + }); + + it("promotes union-global pagination params from the first search request", async () => { + const mockTypesenseClient = { + multiSearch: { + perform: jest.fn().mockResolvedValue({ results: [] }), + }, + }; + + const configuration = new Configuration({ + union: true, + additionalSearchParameters: { + query_by: "name", + offset: 40, + limit: 20, + limit_hits: 200, + }, + }); + + const instantsearchRequests = [ + { + indexName: "products", + params: { query: "test", page: 1, hitsPerPage: 20 }, + }, + { + indexName: "brands", + params: { query: "test", page: 1, hitsPerPage: 20 }, + }, + ]; + + const subject = new SearchRequestAdapter(instantsearchRequests, mockTypesenseClient, configuration); + await subject.request(); + + expect(mockTypesenseClient.multiSearch.perform).toHaveBeenCalledWith( + { + union: true, + searches: [ + { + collection: "products", + q: "test", + page: 2, + per_page: 20, + query_by: "name", + offset: 40, + limit: 20, + limit_hits: 200, + highlight_full_fields: "name", + }, + { + collection: "brands", + q: "test", + page: 2, + per_page: 20, + query_by: "name", + offset: 40, + limit: 20, + limit_hits: 200, + highlight_full_fields: "name", + }, + ], + }, + { + page: 2, + per_page: 20, + offset: 40, + limit: 20, + limit_hits: 200, + }, + ); + }); + it("does not include union parameter in multisearch request when union is false", async () => { const mockTypesenseClient = { multiSearch: { @@ -548,6 +674,7 @@ describe("SearchRequestAdapter", () => { collection: "products", q: "test", page: 1, + per_page: 20, query_by: "name", conversation: true, conversation_id: "conv_123", @@ -563,6 +690,7 @@ describe("SearchRequestAdapter", () => { { collection: "products", page: 1, + per_page: 20, query_by: "name", }, ], @@ -573,6 +701,7 @@ describe("SearchRequestAdapter", () => { conversation_id: "conv_123", conversation_model_id: "model_456", page: 1, + per_page: 20, }, ); });