Skip to content

Commit

Permalink
Add delete documents by filter for Meilisearch v1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
bidoubiwa committed May 25, 2023
1 parent 7f927d0 commit bf30887
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ client.index('myIndex').deleteDocument(documentId: string | number): Promise<Enq
#### [Delete multiple documents](https://docs.meilisearch.com/reference/api/documents.html#delete-documents)

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

#### [Delete all documents](https://docs.meilisearch.com/reference/api/documents.html#delete-all-documents)
Expand Down
38 changes: 31 additions & 7 deletions src/indexes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import {
ResourceResults,
RawDocumentAdditionOptions,
ContentType,
DocumentsIds,
DocumentsDeletionQuery,
} from './types'
import { removeUndefinedFromObject } from './utils'
import { HttpRequests } from './http-requests'
Expand Down Expand Up @@ -526,19 +528,41 @@ class Index<T extends Record<string, any> = Record<string, any>> {
}

/**
* 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<EnqueuedTask> {
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')
}

return new EnqueuedTask(task)
throw e
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ export type DocumentQuery<T = Record<string, any>> = {
fields?: Fields<T>
}

export type DocumentsDeletionQuery = {
filter: Filter
}

export type DocumentsIds = string[] | number[]

/*
** Settings
*/
Expand Down
59 changes: 59 additions & 0 deletions tests/documents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Book>()

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<Book>()

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
Expand Down Expand Up @@ -511,6 +553,23 @@ 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 unknown route`, 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()
Expand Down

0 comments on commit bf30887

Please sign in to comment.