Skip to content

Commit 0456d3e

Browse files
authored
Add filtering on deleteDocuments for Meilisearch v1.2 (#1492)
* Add delete documents by filter for Meilisearch v1.2
1 parent 3a7b03b commit 0456d3e

File tree

4 files changed

+117
-8
lines changed

4 files changed

+117
-8
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ client.index('myIndex').deleteDocument(documentId: string | number): Promise<Enq
471471
#### [Delete multiple documents](https://docs.meilisearch.com/reference/api/documents.html#delete-documents)
472472

473473
```ts
474-
client.index('myIndex').deleteDocuments(documentsIds: string[] | number[]): Promise<EnqueuedTask>
474+
client.index('myIndex').deleteDocuments(params: DocumentsDeletionQuery | DocumentsIds): Promise<EnqueuedTask>
475475
```
476476

477477
#### [Delete all documents](https://docs.meilisearch.com/reference/api/documents.html#delete-all-documents)

src/indexes.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import {
4343
ResourceResults,
4444
RawDocumentAdditionOptions,
4545
ContentType,
46+
DocumentsIds,
47+
DocumentsDeletionQuery,
4648
} from './types'
4749
import { removeUndefinedFromObject } from './utils'
4850
import { HttpRequests } from './http-requests'
@@ -526,19 +528,43 @@ class Index<T extends Record<string, any> = Record<string, any>> {
526528
}
527529

528530
/**
529-
* Delete multiples documents of an index
531+
* Delete multiples documents of an index.
532+
*
533+
* @param params - Params value can be:
534+
*
535+
* - DocumentsDeletionQuery: An object containing the parameters to customize
536+
* your document deletion. Only available in Meilisearch v1.2 and newer
537+
* - DocumentsIds: An array of document ids to delete
530538
*
531-
* @param documentsIds - Array of Document Ids to delete
532539
* @returns Promise containing an EnqueuedTask
533540
*/
534541
async deleteDocuments(
535-
documentsIds: string[] | number[]
542+
params: DocumentsDeletionQuery | DocumentsIds
536543
): Promise<EnqueuedTask> {
537-
const url = `indexes/${this.uid}/documents/delete-batch`
538-
539-
const task = await this.httpRequest.post(url, documentsIds)
544+
// If params is of type DocumentsDeletionQuery
545+
const isDocumentsDeletionQuery =
546+
!Array.isArray(params) && typeof params === 'object'
547+
const endpoint = isDocumentsDeletionQuery
548+
? 'documents/delete'
549+
: 'documents/delete-batch'
550+
const url = `indexes/${this.uid}/${endpoint}`
551+
552+
try {
553+
const task = await this.httpRequest.post(url, params)
554+
555+
return new EnqueuedTask(task)
556+
} catch (e) {
557+
if (
558+
e instanceof MeiliSearchCommunicationError &&
559+
isDocumentsDeletionQuery
560+
) {
561+
e.message = versionErrorHintMessage(e.message, 'deleteDocuments')
562+
} else if (e instanceof MeiliSearchApiError) {
563+
e.message = versionErrorHintMessage(e.message, 'deleteDocuments')
564+
}
540565

541-
return new EnqueuedTask(task)
566+
throw e
567+
}
542568
}
543569

544570
/**

src/types/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,12 @@ export type DocumentQuery<T = Record<string, any>> = {
225225
fields?: Fields<T>
226226
}
227227

228+
export type DocumentsDeletionQuery = {
229+
filter: Filter
230+
}
231+
232+
export type DocumentsIds = string[] | number[]
233+
228234
/*
229235
** Settings
230236
*/

tests/documents.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,48 @@ Hint: It might not be working because maybe you're not up to date with the Meili
470470
expect(response.results.length).toEqual(dataset.length)
471471
})
472472

473+
test(`${permission} key: Delete some documents with string filters`, async () => {
474+
const client = await getClient(permission)
475+
await client.index(indexPk.uid).updateFilterableAttributes(['id'])
476+
const { taskUid: addDocTask } = await client
477+
.index(indexPk.uid)
478+
.addDocuments(dataset)
479+
await client.index(indexPk.uid).waitForTask(addDocTask)
480+
481+
const task = await client
482+
.index(indexPk.uid)
483+
.deleteDocuments({ filter: 'id IN [1, 2]' })
484+
485+
const resolvedTask = await client
486+
.index(indexPk.uid)
487+
.waitForTask(task.taskUid)
488+
const documents = await client.index(indexPk.uid).getDocuments<Book>()
489+
490+
expect(resolvedTask.details.deletedDocuments).toEqual(2)
491+
expect(documents.results.length).toEqual(dataset.length - 2)
492+
})
493+
494+
test(`${permission} key: Delete some documents with array filters`, async () => {
495+
const client = await getClient(permission)
496+
await client.index(indexPk.uid).updateFilterableAttributes(['id'])
497+
const { taskUid: addDocTask } = await client
498+
.index(indexPk.uid)
499+
.addDocuments(dataset)
500+
await client.index(indexPk.uid).waitForTask(addDocTask)
501+
502+
const task = await client
503+
.index(indexPk.uid)
504+
.deleteDocuments({ filter: [['id = 1', 'id = 2']] })
505+
506+
const resolvedTask = await client
507+
.index(indexPk.uid)
508+
.waitForTask(task.taskUid)
509+
const documents = await client.index(indexPk.uid).getDocuments<Book>()
510+
511+
expect(resolvedTask.details.deletedDocuments).toEqual(2)
512+
expect(documents.results.length).toEqual(dataset.length - 2)
513+
})
514+
473515
test(`${permission} key: Delete some documents from index that has NO primary key`, async () => {
474516
const client = await getClient(permission)
475517
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
511553
expect(returnedIds).not.toContain(ids[1])
512554
})
513555

556+
test(`${permission} key: Delete some documents should trigger error with a hint on a MeilisearchApiError`, async () => {
557+
const client = await getClient(permission)
558+
const task = await client.createIndex(indexPk.uid)
559+
await client.waitForTask(task.taskUid)
560+
561+
try {
562+
await client.index(indexPk.uid).deleteDocuments({ filter: '' })
563+
564+
fail(
565+
'deleteDocuments should have raised an error when the parameters are wrong'
566+
)
567+
} catch (e: any) {
568+
expect(e.message).toEqual(
569+
"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."
570+
)
571+
}
572+
})
573+
574+
test(`${permission} key: Delete some documents should trigger error with a hint on a MeilisearchCommunicationError`, async () => {
575+
const apiKey = await getKey(permission)
576+
const client = new MeiliSearch({ host: `${HOST}/indexes`, apiKey })
577+
578+
try {
579+
await client.index(indexPk.uid).deleteDocuments({ filter: 'id = 1' })
580+
581+
fail(
582+
'deleteDocuments should have raised an error when the route does not exist'
583+
)
584+
} catch (e: any) {
585+
expect(e.message).toEqual(
586+
"Not Found\nHint: It might not be working because maybe you're not up to date with the Meilisearch version that deleteDocuments call requires."
587+
)
588+
}
589+
})
590+
514591
test(`${permission} key: Delete all document from index that has NO primary key`, async () => {
515592
const client = await getClient(permission)
516593
const task = await client.index(indexNoPk.uid).deleteAllDocuments()

0 commit comments

Comments
 (0)