diff --git a/README.md b/README.md index 420a24ae4..51b8d3020 100644 --- a/README.md +++ b/README.md @@ -471,7 +471,7 @@ client.index('myIndex').deleteDocument(documentId: string | number): Promise +client.index('myIndex').deleteDocuments(params: DocumentsDeletionQuery | DocumentsIds): Promise ``` #### [Delete all documents](https://docs.meilisearch.com/reference/api/documents.html#delete-all-documents) diff --git a/src/indexes.ts b/src/indexes.ts index deddc11e9..b4f2431c5 100644 --- a/src/indexes.ts +++ b/src/indexes.ts @@ -43,6 +43,8 @@ import { ResourceResults, RawDocumentAdditionOptions, ContentType, + DocumentsIds, + DocumentsDeletionQuery, } from './types' import { removeUndefinedFromObject } from './utils' import { HttpRequests } from './http-requests' @@ -526,19 +528,43 @@ class Index = Record> { } /** - * Delete multiples documents of an index + * Delete multiples documents of an index. + * + * @param params - Params value can be: + * + * - DocumentsDeletionQuery: An object containing the parameters to customize + * your document deletion. Only available in Meilisearch v1.2 and newer + * - DocumentsIds: An array of document ids to delete * - * @param documentsIds - Array of Document Ids to delete * @returns Promise containing an EnqueuedTask */ async deleteDocuments( - documentsIds: string[] | number[] + params: DocumentsDeletionQuery | DocumentsIds ): Promise { - const url = `indexes/${this.uid}/documents/delete-batch` - - const task = await this.httpRequest.post(url, documentsIds) + // If params is of type DocumentsDeletionQuery + const isDocumentsDeletionQuery = + !Array.isArray(params) && typeof params === 'object' + const endpoint = isDocumentsDeletionQuery + ? 'documents/delete' + : 'documents/delete-batch' + const url = `indexes/${this.uid}/${endpoint}` + + try { + const task = await this.httpRequest.post(url, params) + + return new EnqueuedTask(task) + } catch (e) { + if ( + e instanceof MeiliSearchCommunicationError && + isDocumentsDeletionQuery + ) { + e.message = versionErrorHintMessage(e.message, 'deleteDocuments') + } else if (e instanceof MeiliSearchApiError) { + e.message = versionErrorHintMessage(e.message, 'deleteDocuments') + } - return new EnqueuedTask(task) + throw e + } } /** diff --git a/src/types/types.ts b/src/types/types.ts index 0536bd09a..1de2a0087 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -225,6 +225,12 @@ export type DocumentQuery> = { fields?: Fields } +export type DocumentsDeletionQuery = { + filter: Filter +} + +export type DocumentsIds = string[] | number[] + /* ** Settings */ diff --git a/tests/documents.test.ts b/tests/documents.test.ts index 73a42df50..8ae96eac2 100644 --- a/tests/documents.test.ts +++ b/tests/documents.test.ts @@ -470,6 +470,48 @@ Hint: It might not be working because maybe you're not up to date with the Meili expect(response.results.length).toEqual(dataset.length) }) + test(`${permission} key: Delete some documents with string filters`, async () => { + const client = await getClient(permission) + await client.index(indexPk.uid).updateFilterableAttributes(['id']) + const { taskUid: addDocTask } = await client + .index(indexPk.uid) + .addDocuments(dataset) + await client.index(indexPk.uid).waitForTask(addDocTask) + + const task = await client + .index(indexPk.uid) + .deleteDocuments({ filter: 'id IN [1, 2]' }) + + const resolvedTask = await client + .index(indexPk.uid) + .waitForTask(task.taskUid) + const documents = await client.index(indexPk.uid).getDocuments() + + expect(resolvedTask.details.deletedDocuments).toEqual(2) + expect(documents.results.length).toEqual(dataset.length - 2) + }) + + test(`${permission} key: Delete some documents with array filters`, async () => { + const client = await getClient(permission) + await client.index(indexPk.uid).updateFilterableAttributes(['id']) + const { taskUid: addDocTask } = await client + .index(indexPk.uid) + .addDocuments(dataset) + await client.index(indexPk.uid).waitForTask(addDocTask) + + const task = await client + .index(indexPk.uid) + .deleteDocuments({ filter: [['id = 1', 'id = 2']] }) + + const resolvedTask = await client + .index(indexPk.uid) + .waitForTask(task.taskUid) + const documents = await client.index(indexPk.uid).getDocuments() + + expect(resolvedTask.details.deletedDocuments).toEqual(2) + expect(documents.results.length).toEqual(dataset.length - 2) + }) + test(`${permission} key: Delete some documents from index that has NO primary key`, async () => { const client = await getClient(permission) const { taskUid: addDocTask } = await client @@ -511,6 +553,41 @@ Hint: It might not be working because maybe you're not up to date with the Meili expect(returnedIds).not.toContain(ids[1]) }) + test(`${permission} key: Delete some documents should trigger error with a hint on a MeilisearchApiError`, async () => { + const client = await getClient(permission) + const task = await client.createIndex(indexPk.uid) + await client.waitForTask(task.taskUid) + + try { + await client.index(indexPk.uid).deleteDocuments({ filter: '' }) + + fail( + 'deleteDocuments should have raised an error when the parameters are wrong' + ) + } catch (e: any) { + expect(e.message).toEqual( + "Sending an empty filter is forbidden.\nHint: It might not be working because maybe you're not up to date with the Meilisearch version that deleteDocuments call requires." + ) + } + }) + + test(`${permission} key: Delete some documents should trigger error with a hint on a MeilisearchCommunicationError`, async () => { + const apiKey = await getKey(permission) + const client = new MeiliSearch({ host: `${HOST}/indexes`, apiKey }) + + try { + await client.index(indexPk.uid).deleteDocuments({ filter: 'id = 1' }) + + fail( + 'deleteDocuments should have raised an error when the route does not exist' + ) + } catch (e: any) { + expect(e.message).toEqual( + "Not Found\nHint: It might not be working because maybe you're not up to date with the Meilisearch version that deleteDocuments call requires." + ) + } + }) + test(`${permission} key: Delete all document from index that has NO primary key`, async () => { const client = await getClient(permission) const task = await client.index(indexNoPk.uid).deleteAllDocuments()