Skip to content

Commit d725549

Browse files
committed
Add searchCutoffMs index setting
1 parent 406b689 commit d725549

File tree

3 files changed

+275
-0
lines changed

3 files changed

+275
-0
lines changed

src/indexes.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
Dictionary,
5353
ProximityPrecision,
5454
Embedders,
55+
SearchCutoffMsSettings,
5556
} from './types'
5657
import { removeUndefinedFromObject } from './utils'
5758
import { HttpRequests } from './http-requests'
@@ -1334,6 +1335,47 @@ class Index<T extends Record<string, any> = Record<string, any>> {
13341335

13351336
return task
13361337
}
1338+
1339+
///
1340+
/// SEARCHCUTOFFMS SETTINGS
1341+
///
1342+
1343+
/**
1344+
* Get the SearchCutoffMs settings.
1345+
*
1346+
* @returns Promise containing object of SearchCutoffMs settings
1347+
*/
1348+
async getSearchCutoffMs(): Promise<SearchCutoffMsSettings> {
1349+
const url = `indexes/${this.uid}/settings/search-cutoff-ms`
1350+
return await this.httpRequest.get<SearchCutoffMsSettings>(url)
1351+
}
1352+
1353+
/**
1354+
* Update the SearchCutoffMs settings.
1355+
*
1356+
* @param searchCutoffMs - Object containing SearchCutoffMsSettings
1357+
* @returns Promise containing an EnqueuedTask
1358+
*/
1359+
async updateSearchCutoffMs(
1360+
searchCutoffMs: SearchCutoffMsSettings
1361+
): Promise<EnqueuedTask> {
1362+
const url = `indexes/${this.uid}/settings/search-cutoff-ms`
1363+
const task = await this.httpRequest.patch(url, searchCutoffMs)
1364+
1365+
return new EnqueuedTask(task)
1366+
}
1367+
1368+
/**
1369+
* Reset the SearchCutoffMs settings.
1370+
*
1371+
* @returns Promise containing an EnqueuedTask
1372+
*/
1373+
async resetSearchCutoffMs(): Promise<EnqueuedTask> {
1374+
const url = `indexes/${this.uid}/settings/search-cutoff-ms`
1375+
const task = await this.httpRequest.delete(url)
1376+
1377+
return new EnqueuedTask(task)
1378+
}
13371379
}
13381380

13391381
export { Index }

src/types/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,10 @@ export type PaginationSettings = {
373373
maxTotalHits?: number | null
374374
}
375375

376+
export type SearchCutoffMsSettings = {
377+
searchCutoffMs?: number | null
378+
}
379+
376380
export type Settings = {
377381
filterableAttributes?: FilterableAttributes
378382
distinctAttribute?: DistinctAttribute
@@ -390,6 +394,7 @@ export type Settings = {
390394
dictionary?: Dictionary
391395
proximityPrecision?: ProximityPrecision
392396
embedders?: Embedders
397+
searchCutoffMs?: SearchCutoffMsSettings
393398
}
394399

395400
/*
@@ -930,6 +935,9 @@ export const ErrorStatusCode = {
930935
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_pagination */
931936
INVALID_SETTINGS_PAGINATION: 'invalid_settings_pagination',
932937

938+
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_search_cutoff_ms */
939+
INVALID_SETTINGS_SEARCH_CUTOFF_MS: 'invalid_settings_search_cutoff_ms',
940+
933941
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_before_enqueued_at */
934942
INVALID_TASK_BEFORE_ENQUEUED_AT: 'invalid_task_before_enqueued_at',
935943

tests/searchCutoffMs.ts

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import { ErrorStatusCode } from '../src/types'
2+
import {
3+
clearAllIndexes,
4+
config,
5+
BAD_HOST,
6+
MeiliSearch,
7+
getClient,
8+
dataset,
9+
} from './utils/meilisearch-test-utils'
10+
11+
const index = {
12+
uid: 'movies_test',
13+
}
14+
15+
const DEFAULT_SEARCHCUTOFFMS = 1500
16+
17+
jest.setTimeout(100 * 1000)
18+
19+
afterAll(() => {
20+
return clearAllIndexes(config)
21+
})
22+
23+
describe.each([{ permission: 'Master' }, { permission: 'Admin' }])(
24+
'Test on searchCutoffMs',
25+
({ permission }) => {
26+
beforeEach(async () => {
27+
await clearAllIndexes(config)
28+
const client = await getClient('Master')
29+
const { taskUid } = await client.index(index.uid).addDocuments(dataset)
30+
await client.waitForTask(taskUid)
31+
})
32+
33+
test(`${permission} key: Get default searchCutoffMs settings`, async () => {
34+
const client = await getClient(permission)
35+
const response = await client.index(index.uid).getSearchCutoffMs()
36+
37+
expect(response).toEqual({ searchCutoffMs: DEFAULT_SEARCHCUTOFFMS })
38+
})
39+
40+
test(`${permission} key: Update searchCutoffMs to valid value`, async () => {
41+
const client = await getClient(permission)
42+
const newSearchCutoffMs = {
43+
searchCutoffMs: 100,
44+
}
45+
const task = await client
46+
.index(index.uid)
47+
.updateSearchCutoffMs(newSearchCutoffMs)
48+
await client.waitForTask(task.taskUid)
49+
50+
const response = await client.index(index.uid).getSearchCutoffMs()
51+
52+
expect(response).toEqual(newSearchCutoffMs)
53+
})
54+
55+
test(`${permission} key: Update searchCutoffMs to null`, async () => {
56+
const client = await getClient(permission)
57+
const newSearchCutoffMs = {
58+
searchCutoffMs: null,
59+
}
60+
const task = await client
61+
.index(index.uid)
62+
.updateSearchCutoffMs(newSearchCutoffMs)
63+
await client.index(index.uid).waitForTask(task.taskUid)
64+
65+
const response = await client.index(index.uid).getSearchCutoffMs()
66+
67+
expect(response).toEqual({ searchCutoffMs: DEFAULT_SEARCHCUTOFFMS })
68+
})
69+
70+
test(`${permission} key: Update searchCutoffMs with invalid value`, async () => {
71+
const client = await getClient(permission)
72+
const newSearchCutoffMs = {
73+
searchCutoffMs: 'hello', // bad searchCutoffMs value
74+
} as any
75+
76+
await expect(
77+
client.index(index.uid).updateSearchCutoffMs(newSearchCutoffMs)
78+
).rejects.toHaveProperty(
79+
'code',
80+
ErrorStatusCode.INVALID_SETTINGS_SEARCH_CUTOFF_MS
81+
)
82+
})
83+
84+
test(`${permission} key: Reset searchCutoffMs`, async () => {
85+
const client = await getClient(permission)
86+
const newSearchCutoffMs = {
87+
searchCutoffMs: 100,
88+
}
89+
const updateTask = await client
90+
.index(index.uid)
91+
.updateSearchCutoffMs(newSearchCutoffMs)
92+
await client.waitForTask(updateTask.taskUid)
93+
const task = await client.index(index.uid).resetSearchCutoffMs()
94+
await client.waitForTask(task.taskUid)
95+
96+
const response = await client.index(index.uid).getSearchCutoffMs()
97+
98+
expect(response).toEqual({ searchCutoffMs: DEFAULT_SEARCHCUTOFFMS })
99+
})
100+
}
101+
)
102+
103+
describe.each([{ permission: 'Search' }])(
104+
'Test on searchCutoffMs',
105+
({ permission }) => {
106+
beforeEach(async () => {
107+
const client = await getClient('Master')
108+
const { taskUid } = await client.createIndex(index.uid)
109+
await client.waitForTask(taskUid)
110+
})
111+
112+
test(`${permission} key: try to get searchCutoffMs and be denied`, async () => {
113+
const client = await getClient(permission)
114+
await expect(
115+
client.index(index.uid).getSearchCutoffMs()
116+
).rejects.toHaveProperty('code', ErrorStatusCode.INVALID_API_KEY)
117+
})
118+
119+
test(`${permission} key: try to update searchCutoffMs and be denied`, async () => {
120+
const client = await getClient(permission)
121+
await expect(
122+
client.index(index.uid).updateSearchCutoffMs({ searchCutoffMs: 100 })
123+
).rejects.toHaveProperty('code', ErrorStatusCode.INVALID_API_KEY)
124+
})
125+
126+
test(`${permission} key: try to reset searchCutoffMs and be denied`, async () => {
127+
const client = await getClient(permission)
128+
await expect(
129+
client.index(index.uid).resetSearchCutoffMs()
130+
).rejects.toHaveProperty('code', ErrorStatusCode.INVALID_API_KEY)
131+
})
132+
}
133+
)
134+
135+
describe.each([{ permission: 'No' }])(
136+
'Test on searchCutoffMs',
137+
({ permission }) => {
138+
beforeAll(async () => {
139+
const client = await getClient('Master')
140+
const { taskUid } = await client.createIndex(index.uid)
141+
await client.waitForTask(taskUid)
142+
})
143+
144+
test(`${permission} key: try to get searchCutoffMs and be denied`, async () => {
145+
const client = await getClient(permission)
146+
await expect(
147+
client.index(index.uid).getSearchCutoffMs()
148+
).rejects.toHaveProperty(
149+
'code',
150+
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER
151+
)
152+
})
153+
154+
test(`${permission} key: try to update searchCutoffMs and be denied`, async () => {
155+
const client = await getClient(permission)
156+
await expect(
157+
client.index(index.uid).updateSearchCutoffMs({ searchCutoffMs: 100 })
158+
).rejects.toHaveProperty(
159+
'code',
160+
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER
161+
)
162+
})
163+
164+
test(`${permission} key: try to reset searchCutoffMs and be denied`, async () => {
165+
const client = await getClient(permission)
166+
await expect(
167+
client.index(index.uid).resetSearchCutoffMs()
168+
).rejects.toHaveProperty(
169+
'code',
170+
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER
171+
)
172+
})
173+
}
174+
)
175+
176+
describe.each([
177+
{ host: BAD_HOST, trailing: false },
178+
{ host: `${BAD_HOST}/api`, trailing: false },
179+
{ host: `${BAD_HOST}/trailing/`, trailing: true },
180+
])('Tests on url construction', ({ host, trailing }) => {
181+
test(`Test getSearchCutoffMs route`, async () => {
182+
const route = `indexes/${index.uid}/settings/search-cutoff-ms`
183+
const client = new MeiliSearch({ host })
184+
const strippedHost = trailing ? host.slice(0, -1) : host
185+
await expect(
186+
client.index(index.uid).getSearchCutoffMs()
187+
).rejects.toHaveProperty(
188+
'message',
189+
`request to ${strippedHost}/${route} failed, reason: connect ECONNREFUSED ${BAD_HOST.replace(
190+
'http://',
191+
''
192+
)}`
193+
)
194+
})
195+
196+
test(`Test updateSearchCutoffMs route`, async () => {
197+
const route = `indexes/${index.uid}/settings/search-cutoff-ms`
198+
const client = new MeiliSearch({ host })
199+
const strippedHost = trailing ? host.slice(0, -1) : host
200+
await expect(
201+
client.index(index.uid).updateSearchCutoffMs({ searchCutoffMs: null })
202+
).rejects.toHaveProperty(
203+
'message',
204+
`request to ${strippedHost}/${route} failed, reason: connect ECONNREFUSED ${BAD_HOST.replace(
205+
'http://',
206+
''
207+
)}`
208+
)
209+
})
210+
211+
test(`Test resetSearchCutoffMs route`, async () => {
212+
const route = `indexes/${index.uid}/settings/search-cutoff-ms`
213+
const client = new MeiliSearch({ host })
214+
const strippedHost = trailing ? host.slice(0, -1) : host
215+
await expect(
216+
client.index(index.uid).resetSearchCutoffMs()
217+
).rejects.toHaveProperty(
218+
'message',
219+
`request to ${strippedHost}/${route} failed, reason: connect ECONNREFUSED ${BAD_HOST.replace(
220+
'http://',
221+
''
222+
)}`
223+
)
224+
})
225+
})

0 commit comments

Comments
 (0)