Skip to content

Commit 5f75d44

Browse files
committed
feat: add warning for individual pagination with union search
- expose logger property in apicall and multisearch classes - warn when individual search pagination parameters are used with union: true - add helper method to detect pagination parameters in search objects - add test for warning scenarios
1 parent 48ebfe6 commit 5f75d44

File tree

3 files changed

+90
-2
lines changed

3 files changed

+90
-2
lines changed

src/Typesense/ApiCall.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export default class ApiCall implements HttpClient {
103103
private readonly numRetriesPerRequest: number;
104104
private readonly additionalUserHeaders?: Record<string, string>;
105105

106-
private readonly logger: Logger;
106+
readonly logger: Logger;
107107
private currentNodeIndex: number;
108108

109109
constructor(private configuration: Configuration) {

src/Typesense/MultiSearch.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,21 @@ import type {
1818
UnionSearchResponse,
1919
MultiSearchRequestsWithoutUnionSchema,
2020
} from "./Types";
21+
import { Logger } from "loglevel";
2122

2223
const RESOURCEPATH = "/multi_search";
2324

2425
export default class MultiSearch {
2526
private requestWithCache: RequestWithCache;
27+
readonly logger: Logger;
2628

2729
constructor(
2830
private apiCall: ApiCall,
2931
private configuration: Configuration,
3032
private useTextContentType: boolean = false,
3133
) {
3234
this.requestWithCache = new RequestWithCache();
35+
this.logger = this.apiCall.logger;
3336
}
3437

3538
clearCache() {
@@ -74,6 +77,12 @@ export default class MultiSearch {
7477
params.use_cache = true;
7578
}
7679

80+
if (searchRequests.union === true && this.hasAnySearchObjectPagination(searchRequests)) {
81+
this.logger.warn(
82+
"Individual `searches` pagination parameters are ignored when `union: true` is set. Use a top-level pagination parameter instead. See https://typesense.org/docs/29.0/api/federated-multi-search.html#union-search"
83+
);
84+
}
85+
7786
const normalizedSearchRequests: Omit<typeof searchRequests, "searches"> & {
7887
searches: ExtractBaseTypes<SearchParams<T[number], Infix>>[];
7988
} = {
@@ -115,6 +124,10 @@ export default class MultiSearch {
115124
private isStreamingRequest(commonParams: { streamConfig?: unknown }) {
116125
return commonParams.streamConfig !== undefined;
117126
}
127+
128+
private hasAnySearchObjectPagination(searchRequests: MultiSearchRequestsSchema<DocumentSchema, string>) {
129+
return searchRequests.searches.some(search => search.page !== undefined || search.per_page !== undefined || search.offset !== undefined || search.limit !== undefined || search.limit_hits !== undefined);
130+
}
118131
}
119132

120133
export type {

test/Typesense/MultiSearch.spec.ts

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
1+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
22
import { Client as TypesenseClient } from "../../src/Typesense";
33
import { ObjectNotFound } from "../../src/Typesense/Errors";
4+
import logger from "loglevel";
45

56
type MultiSearchResult = {
67
title: string;
@@ -9,6 +10,14 @@ type MultiSearchResult = {
910
};
1011

1112
describe("MultiSearch", function () {
13+
const mockConsole = {
14+
error: vi.fn(),
15+
warn: vi.fn(),
16+
info: vi.fn(),
17+
log: vi.fn(),
18+
debug: vi.fn(),
19+
};
20+
1221
const typesense = new TypesenseClient({
1322
nodes: [
1423
{
@@ -45,6 +54,18 @@ describe("MultiSearch", function () {
4554
];
4655

4756
beforeEach(async function () {
57+
// Setup console mocking
58+
vi.spyOn(console, "error").mockImplementation(mockConsole.error);
59+
vi.spyOn(console, "warn").mockImplementation(mockConsole.warn);
60+
vi.spyOn(console, "info").mockImplementation(mockConsole.info);
61+
vi.spyOn(console, "log").mockImplementation(mockConsole.log);
62+
vi.spyOn(console, "debug").mockImplementation(mockConsole.debug);
63+
64+
typesense.multiSearch.logger.setLevel(logger.levels.INFO);
65+
66+
// Clear any previous mock calls
67+
vi.clearAllMocks();
68+
4869
try {
4970
await typesense.collections(testCollectionName).delete();
5071
} catch (error) {
@@ -218,5 +239,59 @@ describe("MultiSearch", function () {
218239
expect(result.results[0].hits?.length).toBe(0);
219240
expect(result.results[1].hits?.length).toBe(0);
220241
});
242+
243+
it("warns when individual search pagination is used with union: true", async function () {
244+
const searches = {
245+
union: true as const,
246+
searches: [
247+
{ q: "first", query_by: "title", page: 1 },
248+
{ q: "second", query_by: "title", per_page: 10 },
249+
],
250+
};
251+
const commonParams = {
252+
collection: testCollectionName,
253+
};
254+
255+
await typesense.multiSearch.perform(searches, commonParams);
256+
257+
expect(mockConsole.warn).toHaveBeenCalledWith(
258+
"Individual `searches` pagination parameters are ignored when `union: true` is set. Use a top-level pagination parameter instead. See https://typesense.org/docs/29.0/api/federated-multi-search.html#union-search"
259+
);
260+
});
261+
262+
it("does not warn when union: true is used without individual search pagination", async function () {
263+
const searches = {
264+
union: true as const,
265+
searches: [
266+
{ q: "first", query_by: "title" },
267+
{ q: "second", query_by: "title" },
268+
],
269+
};
270+
const commonParams = {
271+
collection: testCollectionName,
272+
page: 1, // top-level pagination is fine
273+
};
274+
275+
await typesense.multiSearch.perform(searches, commonParams);
276+
277+
expect(mockConsole.warn).not.toHaveBeenCalled();
278+
});
279+
280+
it("does not warn when union is false with individual search pagination", async function () {
281+
const searches = {
282+
union: false as const,
283+
searches: [
284+
{ q: "first", query_by: "title", page: 1 },
285+
{ q: "second", query_by: "title", per_page: 10 },
286+
],
287+
};
288+
const commonParams = {
289+
collection: testCollectionName,
290+
};
291+
292+
await typesense.multiSearch.perform(searches, commonParams);
293+
294+
expect(mockConsole.warn).not.toHaveBeenCalled();
295+
});
221296
});
222297
});

0 commit comments

Comments
 (0)