Skip to content

Commit 8dc7e87

Browse files
meili-bors[bot]meili-botbidoubiwa
authored
Merge #1489
1489: Changes related to the next Meilisearch release (v1.2.0) r=bidoubiwa a=meili-bot Related to this issue: meilisearch/integration-guides#261 This PR: - gathers the changes related to the next Meilisearch release (v1.2.0) so that this package is ready when the official release is out. - should pass the tests against the [latest pre-release of Meilisearch](https://github.com/meilisearch/meilisearch/releases). - might eventually contain test failures until the Meilisearch v1.2.0 is out. ⚠️ This PR should NOT be merged until the next release of Meilisearch (v1.2.0) is out. _This PR is auto-generated for the [pre-release week](https://github.com/meilisearch/integration-guides/blob/main/resources/pre-release-week.md) purpose._ Co-authored-by: meili-bot <[email protected]> Co-authored-by: cvermand <[email protected]>
2 parents dd0aac3 + 796ce42 commit 8dc7e87

File tree

7 files changed

+222
-25
lines changed

7 files changed

+222
-25
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = {
55
es2020: true,
66
'jest/globals': true,
77
node: true,
8+
jasmine: true,
89
},
910
extends: [
1011
'eslint:recommended',

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://www.meilisearch.com/docs/reference/api/documents#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://www.meilisearch.com/docs/reference/api/documents#delete-all-documents)

src/errors/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './meilisearch-api-error'
33
export * from './meilisearch-communication-error'
44
export * from './meilisearch-error'
55
export * from './meilisearch-timeout-error'
6+
export * from './version-hint-message'

src/errors/version-hint-message.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function versionErrorHintMessage(message: string, method: string) {
2+
return `${message}\nHint: It might not be working because maybe you're not up to date with the Meilisearch version that ${method} call requires.`
3+
}

src/indexes.ts

Lines changed: 73 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77

88
'use strict'
99

10-
import { MeiliSearchError } from './errors'
11-
10+
import {
11+
MeiliSearchError,
12+
MeiliSearchCommunicationError,
13+
versionErrorHintMessage,
14+
MeiliSearchApiError,
15+
} from './errors'
1216
import {
1317
Config,
1418
SearchResponse,
@@ -39,6 +43,8 @@ import {
3943
ResourceResults,
4044
RawDocumentAdditionOptions,
4145
ContentType,
46+
DocumentsIds,
47+
DocumentsDeletionQuery,
4248
} from './types'
4349
import { removeUndefinedFromObject } from './utils'
4450
import { HttpRequests } from './http-requests'
@@ -302,30 +308,49 @@ class Index<T extends Record<string, any> = Record<string, any>> {
302308
///
303309

304310
/**
305-
* Get documents of an index
311+
* Get documents of an index.
306312
*
307-
* @param parameters - Parameters to browse the documents
308-
* @returns Promise containing Document responses
313+
* @param parameters - Parameters to browse the documents. Parameters can
314+
* contain the `filter` field only available in Meilisearch v1.2 and newer
315+
* @returns Promise containing the returned documents
309316
*/
310317
async getDocuments<D extends Record<string, any> = T>(
311318
parameters: DocumentsQuery<D> = {}
312319
): Promise<ResourceResults<D[]>> {
313-
const url = `indexes/${this.uid}/documents`
314-
315-
const fields = (() => {
316-
if (Array.isArray(parameters?.fields)) {
317-
return parameters?.fields?.join(',')
320+
parameters = removeUndefinedFromObject(parameters)
321+
322+
// In case `filter` is provided, use `POST /documents/fetch`
323+
if (parameters.filter !== undefined) {
324+
try {
325+
const url = `indexes/${this.uid}/documents/fetch`
326+
327+
return await this.httpRequest.post<
328+
DocumentsQuery,
329+
Promise<ResourceResults<D[]>>
330+
>(url, parameters)
331+
} catch (e) {
332+
if (e instanceof MeiliSearchCommunicationError) {
333+
e.message = versionErrorHintMessage(e.message, 'getDocuments')
334+
} else if (e instanceof MeiliSearchApiError) {
335+
e.message = versionErrorHintMessage(e.message, 'getDocuments')
336+
}
337+
338+
throw e
318339
}
319-
return undefined
320-
})()
340+
// Else use `GET /documents` method
341+
} else {
342+
const url = `indexes/${this.uid}/documents`
321343

322-
return await this.httpRequest.get<Promise<ResourceResults<D[]>>>(
323-
url,
324-
removeUndefinedFromObject({
344+
// Transform fields to query parameter string format
345+
const fields = Array.isArray(parameters?.fields)
346+
? { fields: parameters?.fields?.join(',') }
347+
: {}
348+
349+
return await this.httpRequest.get<Promise<ResourceResults<D[]>>>(url, {
325350
...parameters,
326-
fields,
351+
...fields,
327352
})
328-
)
353+
}
329354
}
330355

331356
/**
@@ -503,19 +528,43 @@ class Index<T extends Record<string, any> = Record<string, any>> {
503528
}
504529

505530
/**
506-
* 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
507538
*
508-
* @param documentsIds - Array of Document Ids to delete
509539
* @returns Promise containing an EnqueuedTask
510540
*/
511541
async deleteDocuments(
512-
documentsIds: string[] | number[]
542+
params: DocumentsDeletionQuery | DocumentsIds
513543
): Promise<EnqueuedTask> {
514-
const url = `indexes/${this.uid}/documents/delete-batch`
515-
516-
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+
}
517565

518-
return new EnqueuedTask(task)
566+
throw e
567+
}
519568
}
520569

521570
/**

src/types/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,19 @@ export type RawDocumentAdditionOptions = DocumentOptions & {
218218

219219
export type DocumentsQuery<T = Record<string, any>> = ResourceQuery & {
220220
fields?: Fields<T>
221+
filter?: Filter
221222
}
222223

223224
export type DocumentQuery<T = Record<string, any>> = {
224225
fields?: Fields<T>
225226
}
226227

228+
export type DocumentsDeletionQuery = {
229+
filter: Filter
230+
}
231+
232+
export type DocumentsIds = string[] | number[]
233+
227234
/*
228235
** Settings
229236
*/
@@ -549,6 +556,12 @@ export const enum ErrorStatusCode {
549556
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_offset */
550557
INVALID_DOCUMENT_OFFSET = 'invalid_document_offset',
551558

559+
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_offset */
560+
INVALID_DOCUMENT_FILTER = 'invalid_document_filter',
561+
562+
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_offset */
563+
MISSING_DOCUMENT_FILTER = 'missing_document_filter',
564+
552565
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#payload_too_large */
553566
PAYLOAD_TOO_LARGE = 'payload_too_large',
554567

tests/documents.test.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
getClient,
88
dataset,
99
Book,
10+
getKey,
11+
HOST,
1012
} from './utils/meilisearch-test-utils'
1113

1214
const indexNoPk = {
@@ -140,6 +142,57 @@ describe('Documents tests', () => {
140142
expect(documents.offset).toEqual(2)
141143
})
142144

145+
test(`${permission} key: Get documents with filters`, async () => {
146+
const client = await getClient(permission)
147+
await client.index(indexPk.uid).updateFilterableAttributes(['id'])
148+
const { taskUid } = await client
149+
.index(indexPk.uid)
150+
.addDocuments(dataset)
151+
await client.waitForTask(taskUid)
152+
153+
const documents = await client.index(indexPk.uid).getDocuments<Book>({
154+
filter: [['id = 1', 'id = 2']],
155+
})
156+
157+
expect(documents.results.length).toEqual(2)
158+
})
159+
160+
test(`${permission} key: Get documents should trigger error with a MeilisearchCommunicationError`, async () => {
161+
const apiKey = await getKey(permission)
162+
const client = new MeiliSearch({ host: `${HOST}/indexes`, apiKey })
163+
164+
try {
165+
await client.index(indexPk.uid).getDocuments({ filter: '' })
166+
167+
fail(
168+
'getDocuments should have raised an error when the route does not exist'
169+
)
170+
} catch (e: any) {
171+
expect(e.message).toEqual(
172+
"Not Found\nHint: It might not be working because maybe you're not up to date with the Meilisearch version that getDocuments call requires."
173+
)
174+
}
175+
})
176+
177+
test(`${permission} key: Get documents should trigger error with a hint on a MeilisearchApiError`, async () => {
178+
const apiKey = await getKey(permission)
179+
const client = new MeiliSearch({ host: `${HOST}`, apiKey })
180+
181+
try {
182+
await client.index(indexPk.uid).getDocuments({ filter: 'id = 1' })
183+
184+
fail(
185+
'getDocuments should have raised an error when the filter is badly formatted'
186+
)
187+
} catch (e: any) {
188+
expect(e.message).toEqual(
189+
`Attribute \`id\` is not filterable. This index does not have configured filterable attributes.
190+
1:3 id = 1
191+
Hint: It might not be working because maybe you're not up to date with the Meilisearch version that getDocuments call requires.`
192+
)
193+
}
194+
})
195+
143196
test(`${permission} key: Get documents from index that has NO primary key`, async () => {
144197
const client = await getClient(permission)
145198
const { taskUid } = await client
@@ -417,6 +470,48 @@ describe('Documents tests', () => {
417470
expect(response.results.length).toEqual(dataset.length)
418471
})
419472

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+
420515
test(`${permission} key: Delete some documents from index that has NO primary key`, async () => {
421516
const client = await getClient(permission)
422517
const { taskUid: addDocTask } = await client
@@ -458,6 +553,41 @@ describe('Documents tests', () => {
458553
expect(returnedIds).not.toContain(ids[1])
459554
})
460555

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+
461591
test(`${permission} key: Delete all document from index that has NO primary key`, async () => {
462592
const client = await getClient(permission)
463593
const task = await client.index(indexNoPk.uid).deleteAllDocuments()

0 commit comments

Comments
 (0)