Skip to content

Commit

Permalink
Merge pull request #1643 from amit-ksh/feat/add-searchCutoffMs-index-…
Browse files Browse the repository at this point in the history
…setting

Add searchCutoffMs index setting
  • Loading branch information
mdubus authored Apr 18, 2024
2 parents 18bf948 + d725549 commit b017fca
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/indexes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
Dictionary,
ProximityPrecision,
Embedders,
SearchCutoffMsSettings,
} from './types'
import { removeUndefinedFromObject } from './utils'
import { HttpRequests } from './http-requests'
Expand Down Expand Up @@ -1334,6 +1335,47 @@ class Index<T extends Record<string, any> = Record<string, any>> {

return task
}

///
/// SEARCHCUTOFFMS SETTINGS
///

/**
* Get the SearchCutoffMs settings.
*
* @returns Promise containing object of SearchCutoffMs settings
*/
async getSearchCutoffMs(): Promise<SearchCutoffMsSettings> {
const url = `indexes/${this.uid}/settings/search-cutoff-ms`
return await this.httpRequest.get<SearchCutoffMsSettings>(url)
}

/**
* Update the SearchCutoffMs settings.
*
* @param searchCutoffMs - Object containing SearchCutoffMsSettings
* @returns Promise containing an EnqueuedTask
*/
async updateSearchCutoffMs(
searchCutoffMs: SearchCutoffMsSettings
): Promise<EnqueuedTask> {
const url = `indexes/${this.uid}/settings/search-cutoff-ms`
const task = await this.httpRequest.patch(url, searchCutoffMs)

return new EnqueuedTask(task)
}

/**
* Reset the SearchCutoffMs settings.
*
* @returns Promise containing an EnqueuedTask
*/
async resetSearchCutoffMs(): Promise<EnqueuedTask> {
const url = `indexes/${this.uid}/settings/search-cutoff-ms`
const task = await this.httpRequest.delete(url)

return new EnqueuedTask(task)
}
}

export { Index }
8 changes: 8 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ export type PaginationSettings = {
maxTotalHits?: number | null
}

export type SearchCutoffMsSettings = {
searchCutoffMs?: number | null
}

export type Settings = {
filterableAttributes?: FilterableAttributes
distinctAttribute?: DistinctAttribute
Expand All @@ -390,6 +394,7 @@ export type Settings = {
dictionary?: Dictionary
proximityPrecision?: ProximityPrecision
embedders?: Embedders
searchCutoffMs?: SearchCutoffMsSettings
}

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

/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_search_cutoff_ms */
INVALID_SETTINGS_SEARCH_CUTOFF_MS: 'invalid_settings_search_cutoff_ms',

/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_before_enqueued_at */
INVALID_TASK_BEFORE_ENQUEUED_AT: 'invalid_task_before_enqueued_at',

Expand Down
225 changes: 225 additions & 0 deletions tests/searchCutoffMs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { ErrorStatusCode } from '../src/types'
import {
clearAllIndexes,
config,
BAD_HOST,
MeiliSearch,
getClient,
dataset,
} from './utils/meilisearch-test-utils'

const index = {
uid: 'movies_test',
}

const DEFAULT_SEARCHCUTOFFMS = 1500

jest.setTimeout(100 * 1000)

afterAll(() => {
return clearAllIndexes(config)
})

describe.each([{ permission: 'Master' }, { permission: 'Admin' }])(
'Test on searchCutoffMs',
({ permission }) => {
beforeEach(async () => {
await clearAllIndexes(config)
const client = await getClient('Master')
const { taskUid } = await client.index(index.uid).addDocuments(dataset)
await client.waitForTask(taskUid)
})

test(`${permission} key: Get default searchCutoffMs settings`, async () => {
const client = await getClient(permission)
const response = await client.index(index.uid).getSearchCutoffMs()

expect(response).toEqual({ searchCutoffMs: DEFAULT_SEARCHCUTOFFMS })
})

test(`${permission} key: Update searchCutoffMs to valid value`, async () => {
const client = await getClient(permission)
const newSearchCutoffMs = {
searchCutoffMs: 100,
}
const task = await client
.index(index.uid)
.updateSearchCutoffMs(newSearchCutoffMs)
await client.waitForTask(task.taskUid)

const response = await client.index(index.uid).getSearchCutoffMs()

expect(response).toEqual(newSearchCutoffMs)
})

test(`${permission} key: Update searchCutoffMs to null`, async () => {
const client = await getClient(permission)
const newSearchCutoffMs = {
searchCutoffMs: null,
}
const task = await client
.index(index.uid)
.updateSearchCutoffMs(newSearchCutoffMs)
await client.index(index.uid).waitForTask(task.taskUid)

const response = await client.index(index.uid).getSearchCutoffMs()

expect(response).toEqual({ searchCutoffMs: DEFAULT_SEARCHCUTOFFMS })
})

test(`${permission} key: Update searchCutoffMs with invalid value`, async () => {
const client = await getClient(permission)
const newSearchCutoffMs = {
searchCutoffMs: 'hello', // bad searchCutoffMs value
} as any

await expect(
client.index(index.uid).updateSearchCutoffMs(newSearchCutoffMs)
).rejects.toHaveProperty(
'code',
ErrorStatusCode.INVALID_SETTINGS_SEARCH_CUTOFF_MS
)
})

test(`${permission} key: Reset searchCutoffMs`, async () => {
const client = await getClient(permission)
const newSearchCutoffMs = {
searchCutoffMs: 100,
}
const updateTask = await client
.index(index.uid)
.updateSearchCutoffMs(newSearchCutoffMs)
await client.waitForTask(updateTask.taskUid)
const task = await client.index(index.uid).resetSearchCutoffMs()
await client.waitForTask(task.taskUid)

const response = await client.index(index.uid).getSearchCutoffMs()

expect(response).toEqual({ searchCutoffMs: DEFAULT_SEARCHCUTOFFMS })
})
}
)

describe.each([{ permission: 'Search' }])(
'Test on searchCutoffMs',
({ permission }) => {
beforeEach(async () => {
const client = await getClient('Master')
const { taskUid } = await client.createIndex(index.uid)
await client.waitForTask(taskUid)
})

test(`${permission} key: try to get searchCutoffMs and be denied`, async () => {
const client = await getClient(permission)
await expect(
client.index(index.uid).getSearchCutoffMs()
).rejects.toHaveProperty('code', ErrorStatusCode.INVALID_API_KEY)
})

test(`${permission} key: try to update searchCutoffMs and be denied`, async () => {
const client = await getClient(permission)
await expect(
client.index(index.uid).updateSearchCutoffMs({ searchCutoffMs: 100 })
).rejects.toHaveProperty('code', ErrorStatusCode.INVALID_API_KEY)
})

test(`${permission} key: try to reset searchCutoffMs and be denied`, async () => {
const client = await getClient(permission)
await expect(
client.index(index.uid).resetSearchCutoffMs()
).rejects.toHaveProperty('code', ErrorStatusCode.INVALID_API_KEY)
})
}
)

describe.each([{ permission: 'No' }])(
'Test on searchCutoffMs',
({ permission }) => {
beforeAll(async () => {
const client = await getClient('Master')
const { taskUid } = await client.createIndex(index.uid)
await client.waitForTask(taskUid)
})

test(`${permission} key: try to get searchCutoffMs and be denied`, async () => {
const client = await getClient(permission)
await expect(
client.index(index.uid).getSearchCutoffMs()
).rejects.toHaveProperty(
'code',
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER
)
})

test(`${permission} key: try to update searchCutoffMs and be denied`, async () => {
const client = await getClient(permission)
await expect(
client.index(index.uid).updateSearchCutoffMs({ searchCutoffMs: 100 })
).rejects.toHaveProperty(
'code',
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER
)
})

test(`${permission} key: try to reset searchCutoffMs and be denied`, async () => {
const client = await getClient(permission)
await expect(
client.index(index.uid).resetSearchCutoffMs()
).rejects.toHaveProperty(
'code',
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER
)
})
}
)

describe.each([
{ host: BAD_HOST, trailing: false },
{ host: `${BAD_HOST}/api`, trailing: false },
{ host: `${BAD_HOST}/trailing/`, trailing: true },
])('Tests on url construction', ({ host, trailing }) => {
test(`Test getSearchCutoffMs route`, async () => {
const route = `indexes/${index.uid}/settings/search-cutoff-ms`
const client = new MeiliSearch({ host })
const strippedHost = trailing ? host.slice(0, -1) : host
await expect(
client.index(index.uid).getSearchCutoffMs()
).rejects.toHaveProperty(
'message',
`request to ${strippedHost}/${route} failed, reason: connect ECONNREFUSED ${BAD_HOST.replace(
'http://',
''
)}`
)
})

test(`Test updateSearchCutoffMs route`, async () => {
const route = `indexes/${index.uid}/settings/search-cutoff-ms`
const client = new MeiliSearch({ host })
const strippedHost = trailing ? host.slice(0, -1) : host
await expect(
client.index(index.uid).updateSearchCutoffMs({ searchCutoffMs: null })
).rejects.toHaveProperty(
'message',
`request to ${strippedHost}/${route} failed, reason: connect ECONNREFUSED ${BAD_HOST.replace(
'http://',
''
)}`
)
})

test(`Test resetSearchCutoffMs route`, async () => {
const route = `indexes/${index.uid}/settings/search-cutoff-ms`
const client = new MeiliSearch({ host })
const strippedHost = trailing ? host.slice(0, -1) : host
await expect(
client.index(index.uid).resetSearchCutoffMs()
).rejects.toHaveProperty(
'message',
`request to ${strippedHost}/${route} failed, reason: connect ECONNREFUSED ${BAD_HOST.replace(
'http://',
''
)}`
)
})
})

0 comments on commit b017fca

Please sign in to comment.