From 727e3f71509c93eebf36c217861bb5a343071389 Mon Sep 17 00:00:00 2001 From: meili-bot <74670311+meili-bot@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:57:41 +0200 Subject: [PATCH 01/14] Update README.md From 574e5ef7c2726763ca39c56a09f1bccd1a93537e Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:09:52 +0300 Subject: [PATCH 02/14] Add lang settings and search params, add test --- src/types/types.ts | 12 ++++++++++++ tests/search.test.ts | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/types/types.ts b/src/types/types.ts index 7406b88c0..ad297bcd8 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -130,6 +130,9 @@ export type SearchParams = Query & hybrid?: HybridSearch; distinct?: string; retrieveVectors?: boolean; + // @TODO: Either explicitly type this (that implies keeping it up to date), + // or link to the docs where the available locales are listed + locales?: string[]; }; // Search parameters for searches made with the GET method @@ -428,6 +431,14 @@ export type PaginationSettings = { export type SearchCutoffMs = number | null; +export type LocalizedAttribute = { + attributePatterns: string[]; + // @TODO: Type or link to docs + locales: string[]; +}; + +export type LocalizedAttributes = LocalizedAttribute[] | null; + export type Settings = { filterableAttributes?: FilterableAttributes; distinctAttribute?: DistinctAttribute; @@ -446,6 +457,7 @@ export type Settings = { proximityPrecision?: ProximityPrecision; embedders?: Embedders; searchCutoffMs?: SearchCutoffMs; + localizedAttributes?: LocalizedAttributes; }; /* diff --git a/tests/search.test.ts b/tests/search.test.ts index 57aca68e5..b0764f018 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -992,6 +992,30 @@ describe.each([ client.index(index.uid).search('prince', {}), ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INDEX_NOT_FOUND); }); + + test.only(`${permission} key: Search with locales`, async () => { + const ind = (await getClient(permission)).index(index.uid), + masterInd = (await getClient('Master')).index(index.uid); + + await masterInd.waitForTask( + ( + await masterInd.updateSettings({ + localizedAttributes: [ + { + attributePatterns: ['title', 'comment'], + locales: ['fra', 'eng'], + }, + ], + }) + ).taskUid, + ); + + const response = await ind.search('french', { + locales: ['fra', 'eng'], + }); + + expect(response.hits.length).toEqual(2); + }); }); describe.each([{ permission: 'No' }])( From c3b713e27a0b7109f4f30602aad86ad18cc02993 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:12:38 +0300 Subject: [PATCH 03/14] Remove test focus --- tests/search.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/search.test.ts b/tests/search.test.ts index b0764f018..229b14fbc 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -993,7 +993,7 @@ describe.each([ ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INDEX_NOT_FOUND); }); - test.only(`${permission} key: Search with locales`, async () => { + test(`${permission} key: Search with locales`, async () => { const ind = (await getClient(permission)).index(index.uid), masterInd = (await getClient('Master')).index(index.uid); From e701bc14b24444e4a6a5c4e95bb85befb47d60cb Mon Sep 17 00:00:00 2001 From: Morgane Dubus <30866152+mdubus@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:19:57 +0200 Subject: [PATCH 04/14] feat(v1.10): AI-powered search changes (#1692) --- src/types/types.ts | 10 +++++----- tests/embedders.test.ts | 26 ++++++++++++++++++-------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/types/types.ts b/src/types/types.ts index 7406b88c0..5660935b7 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -366,6 +366,7 @@ export type OpenAiEmbedder = { documentTemplate?: string; dimensions?: number; distribution?: Distribution; + url?: string; }; export type HuggingFaceEmbedder = { @@ -388,12 +389,10 @@ export type RestEmbedder = { apiKey?: string; dimensions?: number; documentTemplate?: string; - inputField?: string[] | null; - inputType?: 'text' | 'textArray'; - query?: Record | null; - pathToEmbeddings?: string[] | null; - embeddingObject?: string[] | null; distribution?: Distribution; + request: Record; + response: Record; + headers?: Record; }; export type OllamaEmbedder = { @@ -403,6 +402,7 @@ export type OllamaEmbedder = { model?: string; documentTemplate?: string; distribution?: Distribution; + dimensions?: number; }; export type Embedder = diff --git a/tests/embedders.test.ts b/tests/embedders.test.ts index 73d58745c..68d927d9c 100644 --- a/tests/embedders.test.ts +++ b/tests/embedders.test.ts @@ -118,6 +118,7 @@ describe.each([{ permission: 'Master' }, { permission: 'Admin' }])( mean: 0.7, sigma: 0.3, }, + url: 'https://api.openai.com/v1/embeddings', }, }; const task: EnqueuedTask = await client @@ -169,17 +170,25 @@ describe.each([{ permission: 'Master' }, { permission: 'Admin' }])( dimensions: 1536, documentTemplate: "A movie titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}", - inputField: ['input'], - inputType: 'textArray', - query: { - model: 'text-embedding-ada-002', - }, - pathToEmbeddings: ['data'], - embeddingObject: ['embedding'], distribution: { mean: 0.7, sigma: 0.3, }, + request: { + model: 'text-embedding-3-small', + input: ['{{text}}', '{{..}}'], + }, + response: { + data: [ + { + embedding: '{{embedding}}', + }, + '{{..}}', + ], + }, + headers: { + 'Custom-Header': 'CustomValue', + }, }, }; const task: EnqueuedTask = await client @@ -197,7 +206,7 @@ describe.each([{ permission: 'Master' }, { permission: 'Admin' }])( }); }); - test.skip(`${permission} key: Update embedders with 'ollama' source`, async () => { + test(`${permission} key: Update embedders with 'ollama' source`, async () => { const client = await getClient(permission); const newEmbedder: Embedders = { default: { @@ -210,6 +219,7 @@ describe.each([{ permission: 'Master' }, { permission: 'Admin' }])( mean: 0.7, sigma: 0.3, }, + dimensions: 512, }, }; const task: EnqueuedTask = await client From d987c639281d289223c8034c630da23e78fe5272 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Tue, 20 Aug 2024 23:11:00 +0300 Subject: [PATCH 05/14] Add settings/localized-attributes methods and tests, adjust tests, add locale types --- src/indexes.ts | 42 ++++ src/types/types.ts | 82 ++++++- tests/localized_attributes.test.ts | 212 ++++++++++++++++++ tests/search.test.ts | 27 +-- ...chCutoffMs.ts => search_cutoff_ms.test.ts} | 0 5 files changed, 342 insertions(+), 21 deletions(-) create mode 100644 tests/localized_attributes.test.ts rename tests/{searchCutoffMs.ts => search_cutoff_ms.test.ts} (100%) diff --git a/src/indexes.ts b/src/indexes.ts index 85c30bf62..048812d27 100644 --- a/src/indexes.ts +++ b/src/indexes.ts @@ -54,6 +54,7 @@ import { Embedders, SearchCutoffMs, SearchSimilarDocumentsParams, + LocalizedAttributes, } from './types'; import { removeUndefinedFromObject } from './utils'; import { HttpRequests } from './http-requests'; @@ -1393,6 +1394,47 @@ class Index = Record> { return new EnqueuedTask(task); } + + /// + /// LOCALIZED ATTRIBUTES SETTINGS + /// + + /** + * Get the localized attributes settings. + * + * @returns Promise containing object of localized attributes settings + */ + async getLocalizedAttributes(): Promise { + const url = `indexes/${this.uid}/settings/localized-attributes`; + return await this.httpRequest.get(url); + } + + /** + * Update the localized attributes settings. + * + * @param localizedAttributes - Localized attributes object + * @returns Promise containing an EnqueuedTask + */ + async updateLocalizedAttributes( + localizedAttributes: LocalizedAttributes, + ): Promise { + const url = `indexes/${this.uid}/settings/localized-attributes`; + const task = await this.httpRequest.put(url, localizedAttributes); + + return new EnqueuedTask(task); + } + + /** + * Reset the localized attributes settings. + * + * @returns Promise containing an EnqueuedTask + */ + async resetLocalizedAttributes(): Promise { + const url = `indexes/${this.uid}/settings/localized-attributes`; + const task = await this.httpRequest.delete(url); + + return new EnqueuedTask(task); + } } export { Index }; diff --git a/src/types/types.ts b/src/types/types.ts index ad297bcd8..a5cde99b9 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -108,6 +108,77 @@ export type HybridSearch = { semanticRatio?: number; }; +export type Locale = + | 'epo' + | 'eng' + | 'rus' + | 'cmn' + | 'spa' + | 'por' + | 'ita' + | 'ben' + | 'fra' + | 'deu' + | 'ukr' + | 'kat' + | 'ara' + | 'hin' + | 'jpn' + | 'heb' + | 'yid' + | 'pol' + | 'amh' + | 'jav' + | 'kor' + | 'nob' + | 'dan' + | 'swe' + | 'fin' + | 'tur' + | 'nld' + | 'hun' + | 'ces' + | 'ell' + | 'bul' + | 'bel' + | 'mar' + | 'kan' + | 'ron' + | 'slv' + | 'hrv' + | 'srp' + | 'mkd' + | 'lit' + | 'lav' + | 'est' + | 'tam' + | 'vie' + | 'urd' + | 'tha' + | 'guj' + | 'uzb' + | 'pan' + | 'aze' + | 'ind' + | 'tel' + | 'pes' + | 'mal' + | 'ori' + | 'mya' + | 'nep' + | 'sin' + | 'khm' + | 'tuk' + | 'aka' + | 'zul' + | 'sna' + | 'afr' + | 'lat' + | 'slk' + | 'cat' + | 'tgl' + | 'hye'; + export type SearchParams = Query & Pagination & Highlight & @@ -130,9 +201,7 @@ export type SearchParams = Query & hybrid?: HybridSearch; distinct?: string; retrieveVectors?: boolean; - // @TODO: Either explicitly type this (that implies keeping it up to date), - // or link to the docs where the available locales are listed - locales?: string[]; + locales?: Locale[]; }; // Search parameters for searches made with the GET method @@ -433,8 +502,7 @@ export type SearchCutoffMs = number | null; export type LocalizedAttribute = { attributePatterns: string[]; - // @TODO: Type or link to docs - locales: string[]; + locales: Locale[]; }; export type LocalizedAttributes = LocalizedAttribute[] | null; @@ -1004,6 +1072,10 @@ export const ErrorStatusCode = { /** @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_settings_search_cutoff_ms */ + INVALID_SETTINGS_LOCALIZED_ATTRIBUTES: + 'invalid_settings_localized_attributes', + /** @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', diff --git a/tests/localized_attributes.test.ts b/tests/localized_attributes.test.ts new file mode 100644 index 000000000..42dcd380a --- /dev/null +++ b/tests/localized_attributes.test.ts @@ -0,0 +1,212 @@ +import { ErrorStatusCode, type LocalizedAttributes } from '../src/types'; +import { + clearAllIndexes, + config, + BAD_HOST, + MeiliSearch, + getClient, + dataset, +} from './utils/meilisearch-test-utils'; + +const index = { + uid: 'movies_test', +}; + +const DEFAULT_LOCALIZED_ATTRIBUTES = null; + +jest.setTimeout(100 * 1000); + +afterAll(() => { + return clearAllIndexes(config); +}); + +describe.each([{ permission: 'Master' }, { permission: 'Admin' }])( + 'Test on localizedAttributes', + ({ 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 localizedAttributes settings`, async () => { + const client = await getClient(permission); + const response = await client.index(index.uid).getLocalizedAttributes(); + + expect(response).toEqual(DEFAULT_LOCALIZED_ATTRIBUTES); + }); + + test(`${permission} key: Update localizedAttributes to valid value`, async () => { + const client = await getClient(permission); + const newLocalizedAttributes: LocalizedAttributes = [ + { attributePatterns: ['title'], locales: ['eng'] }, + ]; + const task = await client + .index(index.uid) + .updateLocalizedAttributes(newLocalizedAttributes); + await client.waitForTask(task.taskUid); + + const response = await client.index(index.uid).getLocalizedAttributes(); + + expect(response).toEqual(newLocalizedAttributes); + }); + + test(`${permission} key: Update localizedAttributes to null`, async () => { + const client = await getClient(permission); + const newLocalizedAttributes = null; + const task = await client + .index(index.uid) + .updateLocalizedAttributes(newLocalizedAttributes); + await client.index(index.uid).waitForTask(task.taskUid); + + const response = await client.index(index.uid).getLocalizedAttributes(); + + expect(response).toEqual(DEFAULT_LOCALIZED_ATTRIBUTES); + }); + + test(`${permission} key: Update localizedAttributes with invalid value`, async () => { + const client = await getClient(permission); + const newLocalizedAttributes = 'hello' as any; // bad localizedAttributes value + + await expect( + client + .index(index.uid) + .updateLocalizedAttributes(newLocalizedAttributes), + ).rejects.toHaveProperty( + 'cause.code', + ErrorStatusCode.INVALID_SETTINGS_LOCALIZED_ATTRIBUTES, + ); + }); + + test(`${permission} key: Reset localizedAttributes`, async () => { + const client = await getClient(permission); + const newLocalizedAttributes: LocalizedAttributes = []; + const updateTask = await client + .index(index.uid) + .updateLocalizedAttributes(newLocalizedAttributes); + await client.waitForTask(updateTask.taskUid); + const task = await client.index(index.uid).resetLocalizedAttributes(); + await client.waitForTask(task.taskUid); + + const response = await client.index(index.uid).getLocalizedAttributes(); + + expect(response).toEqual(DEFAULT_LOCALIZED_ATTRIBUTES); + }); + }, +); + +describe.each([{ permission: 'Search' }])( + 'Test on localizedAttributes', + ({ 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 localizedAttributes and be denied`, async () => { + const client = await getClient(permission); + await expect( + client.index(index.uid).getLocalizedAttributes(), + ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INVALID_API_KEY); + }); + + test(`${permission} key: try to update localizedAttributes and be denied`, async () => { + const client = await getClient(permission); + await expect( + client.index(index.uid).updateLocalizedAttributes([]), + ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INVALID_API_KEY); + }); + + test(`${permission} key: try to reset localizedAttributes and be denied`, async () => { + const client = await getClient(permission); + await expect( + client.index(index.uid).resetLocalizedAttributes(), + ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INVALID_API_KEY); + }); + }, +); + +describe.each([{ permission: 'No' }])( + 'Test on localizedAttributes', + ({ 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 localizedAttributes and be denied`, async () => { + const client = await getClient(permission); + await expect( + client.index(index.uid).getLocalizedAttributes(), + ).rejects.toHaveProperty( + 'cause.code', + ErrorStatusCode.MISSING_AUTHORIZATION_HEADER, + ); + }); + + test(`${permission} key: try to update localizedAttributes and be denied`, async () => { + const client = await getClient(permission); + await expect( + client.index(index.uid).updateLocalizedAttributes([]), + ).rejects.toHaveProperty( + 'cause.code', + ErrorStatusCode.MISSING_AUTHORIZATION_HEADER, + ); + }); + + test(`${permission} key: try to reset localizedAttributes and be denied`, async () => { + const client = await getClient(permission); + await expect( + client.index(index.uid).resetLocalizedAttributes(), + ).rejects.toHaveProperty( + 'cause.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 getLocalizedAttributes route`, async () => { + const route = `indexes/${index.uid}/settings/localized-attributes`; + const client = new MeiliSearch({ host }); + const strippedHost = trailing ? host.slice(0, -1) : host; + await expect( + client.index(index.uid).getLocalizedAttributes(), + ).rejects.toHaveProperty( + 'message', + `Request to ${strippedHost}/${route} has failed`, + ); + }); + + test(`Test updateLocalizedAttributes route`, async () => { + const route = `indexes/${index.uid}/settings/localized-attributes`; + const client = new MeiliSearch({ host }); + const strippedHost = trailing ? host.slice(0, -1) : host; + await expect( + client.index(index.uid).updateLocalizedAttributes(null), + ).rejects.toHaveProperty( + 'message', + `Request to ${strippedHost}/${route} has failed`, + ); + }); + + test(`Test resetLocalizedAttributes route`, async () => { + const route = `indexes/${index.uid}/settings/localized-attributes`; + const client = new MeiliSearch({ host }); + const strippedHost = trailing ? host.slice(0, -1) : host; + await expect( + client.index(index.uid).resetLocalizedAttributes(), + ).rejects.toHaveProperty( + 'message', + `Request to ${strippedHost}/${route} has failed`, + ); + }); +}); diff --git a/tests/search.test.ts b/tests/search.test.ts index 229b14fbc..362fec216 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -994,27 +994,22 @@ describe.each([ }); test(`${permission} key: Search with locales`, async () => { - const ind = (await getClient(permission)).index(index.uid), - masterInd = (await getClient('Master')).index(index.uid); - - await masterInd.waitForTask( - ( - await masterInd.updateSettings({ - localizedAttributes: [ - { - attributePatterns: ['title', 'comment'], - locales: ['fra', 'eng'], - }, - ], - }) - ).taskUid, + const localIndex = (await getClient(permission)).index(index.uid); + const localMasterIndex = (await getClient('Master')).index(index.uid); + + const updateLocalizedAttributesEnqueuedTask = + await localMasterIndex.updateLocalizedAttributes([ + { attributePatterns: ['title', 'comment'], locales: ['fra', 'eng'] }, + ]); + await localMasterIndex.waitForTask( + updateLocalizedAttributesEnqueuedTask.taskUid, ); - const response = await ind.search('french', { + const searchResponse = await localIndex.search('french', { locales: ['fra', 'eng'], }); - expect(response.hits.length).toEqual(2); + expect(searchResponse.hits.length).toEqual(2); }); }); diff --git a/tests/searchCutoffMs.ts b/tests/search_cutoff_ms.test.ts similarity index 100% rename from tests/searchCutoffMs.ts rename to tests/search_cutoff_ms.test.ts From a0a27cc467b5bb5b572ac428e6cf1652f8664075 Mon Sep 17 00:00:00 2001 From: curquiza Date: Wed, 21 Aug 2024 00:02:39 +0200 Subject: [PATCH 06/14] Update code samples --- .code-samples.meilisearch.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 9271498bd..cb9835ebb 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -773,3 +773,29 @@ distinct_attribute_guide_filterable_1: |- client.index('products').updateFilterableAttributes(['product_id', 'sku', 'url']) distinct_attribute_guide_distinct_parameter_1: |- client.index('products').search('white shirt', { distinct: 'sku' }) +multi_search_federated_1: |- + client.multiSearch({ + federation: {}, + queries: [ + { + indexUid: 'movies', + q: 'batman', + limit: 5, + }, + { + indexUid: 'comics', + q: 'batman', + limit: 5, + }, + ] + }) +search_parameter_reference_locales_1: |- + client.index('INDEX_NAME').search('進撃の巨人', { locales: ['jpn'] }) +get_localized_attribute_settings_1: |- + client.index('INDEX_NAME').getLocalizedAttributes() +update_localized_attribute_settings_1: |- + client.index('INDEX_NAME').updateLocalizedAttributes([ + { attributePatterns: ['jpn'], locales: ['*_ja'] }, + ];) +reset_localized_attribute_settings_1: |- + client.index('INDEX_NAME').resetLocalizedAttributes() From d44a2d6a32447071302cbe77ecfecd6a1810edf3 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:04:29 +0300 Subject: [PATCH 07/14] Removed explicitly typed locales --- src/types/types.ts | 76 +++------------------------------------------- 1 file changed, 4 insertions(+), 72 deletions(-) diff --git a/src/types/types.ts b/src/types/types.ts index a5cde99b9..21acf348c 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -108,76 +108,8 @@ export type HybridSearch = { semanticRatio?: number; }; -export type Locale = - | 'epo' - | 'eng' - | 'rus' - | 'cmn' - | 'spa' - | 'por' - | 'ita' - | 'ben' - | 'fra' - | 'deu' - | 'ukr' - | 'kat' - | 'ara' - | 'hin' - | 'jpn' - | 'heb' - | 'yid' - | 'pol' - | 'amh' - | 'jav' - | 'kor' - | 'nob' - | 'dan' - | 'swe' - | 'fin' - | 'tur' - | 'nld' - | 'hun' - | 'ces' - | 'ell' - | 'bul' - | 'bel' - | 'mar' - | 'kan' - | 'ron' - | 'slv' - | 'hrv' - | 'srp' - | 'mkd' - | 'lit' - | 'lav' - | 'est' - | 'tam' - | 'vie' - | 'urd' - | 'tha' - | 'guj' - | 'uzb' - | 'pan' - | 'aze' - | 'ind' - | 'tel' - | 'pes' - | 'mal' - | 'ori' - | 'mya' - | 'nep' - | 'sin' - | 'khm' - | 'tuk' - | 'aka' - | 'zul' - | 'sna' - | 'afr' - | 'lat' - | 'slk' - | 'cat' - | 'tgl' - | 'hye'; +// @TODO: Add documentation link to available locales +export type Locale = string; export type SearchParams = Query & Pagination & @@ -757,9 +689,9 @@ export interface FetchError extends Error { export type MeiliSearchErrorResponse = { message: string; - // @TODO: Could be typed, but will it be kept updated? https://www.meilisearch.com/docs/reference/errors/error_codes + // https://www.meilisearch.com/docs/reference/errors/error_codes code: string; - // @TODO: Could be typed https://www.meilisearch.com/docs/reference/errors/overview#errors + // https://www.meilisearch.com/docs/reference/errors/overview#errors type: string; link: string; }; From 238200b8ec5e30be93fda12eb05814ac76230c2a Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:34:23 +0300 Subject: [PATCH 08/14] Make test more consistent with the rest, add locales type to SearchRequestGET --- src/types/types.ts | 3 ++- tests/search.test.ts | 17 ++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/types/types.ts b/src/types/types.ts index 21acf348c..1e87faa51 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -108,7 +108,7 @@ export type HybridSearch = { semanticRatio?: number; }; -// @TODO: Add documentation link to available locales +// https://www.meilisearch.com/docs/reference/api/settings#localized-attributes export type Locale = string; export type SearchParams = Query & @@ -156,6 +156,7 @@ export type SearchRequestGET = Pagination & rankingScoreThreshold?: number; distinct?: string; retrieveVectors?: boolean; + locales?: Locale[]; }; export type MultiSearchQuery = SearchParams & { indexUid: string }; diff --git a/tests/search.test.ts b/tests/search.test.ts index 362fec216..45cbb3e48 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -993,19 +993,18 @@ describe.each([ ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INDEX_NOT_FOUND); }); - test(`${permission} key: Search with locales`, async () => { - const localIndex = (await getClient(permission)).index(index.uid); - const localMasterIndex = (await getClient('Master')).index(index.uid); + test.only(`${permission} key: Search with locales`, async () => { + const client = await getClient(permission); + const masterClient = await getClient('Master'); - const updateLocalizedAttributesEnqueuedTask = - await localMasterIndex.updateLocalizedAttributes([ + const { taskUid } = await masterClient + .index(index.uid) + .updateLocalizedAttributes([ { attributePatterns: ['title', 'comment'], locales: ['fra', 'eng'] }, ]); - await localMasterIndex.waitForTask( - updateLocalizedAttributesEnqueuedTask.taskUid, - ); + await masterClient.waitForTask(taskUid); - const searchResponse = await localIndex.search('french', { + const searchResponse = await client.index(index.uid).search('french', { locales: ['fra', 'eng'], }); From c8688b3503386a3e0285c01c169cbf59356b0fd8 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:35:14 +0300 Subject: [PATCH 09/14] Remove test focus --- tests/search.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/search.test.ts b/tests/search.test.ts index 45cbb3e48..3f5c43911 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -993,7 +993,7 @@ describe.each([ ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INDEX_NOT_FOUND); }); - test.only(`${permission} key: Search with locales`, async () => { + test(`${permission} key: Search with locales`, async () => { const client = await getClient(permission); const masterClient = await getClient('Master'); From df7248be1b283ed257ea30ca607d2882c13bb0f8 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:37:16 +0300 Subject: [PATCH 10/14] Fix test --- tests/search.test.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/search.test.ts b/tests/search.test.ts index 3f5c43911..8ff3a173c 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -982,17 +982,6 @@ describe.each([ expect(response.hits[0]).not.toHaveProperty('_vectors'); }); - test(`${permission} key: Try to search on deleted index and fail`, async () => { - const client = await getClient(permission); - const masterClient = await getClient('Master'); - const { taskUid } = await masterClient.index(index.uid).delete(); - await masterClient.waitForTask(taskUid); - - await expect( - client.index(index.uid).search('prince', {}), - ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INDEX_NOT_FOUND); - }); - test(`${permission} key: Search with locales`, async () => { const client = await getClient(permission); const masterClient = await getClient('Master'); @@ -1010,6 +999,17 @@ describe.each([ expect(searchResponse.hits.length).toEqual(2); }); + + test(`${permission} key: Try to search on deleted index and fail`, async () => { + const client = await getClient(permission); + const masterClient = await getClient('Master'); + const { taskUid } = await masterClient.index(index.uid).delete(); + await masterClient.waitForTask(taskUid); + + await expect( + client.index(index.uid).search('prince', {}), + ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INDEX_NOT_FOUND); + }); }); describe.each([{ permission: 'No' }])( From 7780fdc639e1e22aca2bf488d45c0646b2d4f2f9 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:57:14 +0300 Subject: [PATCH 11/14] Update snapshots --- tests/__snapshots__/faceting.test.ts.snap | 75 -- tests/__snapshots__/settings.test.ts.snap | 1089 --------------------- 2 files changed, 1164 deletions(-) delete mode 100644 tests/__snapshots__/faceting.test.ts.snap delete mode 100644 tests/__snapshots__/settings.test.ts.snap diff --git a/tests/__snapshots__/faceting.test.ts.snap b/tests/__snapshots__/faceting.test.ts.snap deleted file mode 100644 index afeb5fc50..000000000 --- a/tests/__snapshots__/faceting.test.ts.snap +++ /dev/null @@ -1,75 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Test on faceting Admin key: Get default faceting object 1`] = ` -{ - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, -} -`; - -exports[`Test on faceting Admin key: Reset faceting 1`] = ` -{ - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, -} -`; - -exports[`Test on faceting Admin key: Update faceting at null 1`] = ` -{ - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, -} -`; - -exports[`Test on faceting Admin key: Update faceting settings 1`] = ` -{ - "maxValuesPerFacet": 12, - "sortFacetValuesBy": { - "*": "alpha", - "test": "count", - }, -} -`; - -exports[`Test on faceting Master key: Get default faceting object 1`] = ` -{ - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, -} -`; - -exports[`Test on faceting Master key: Reset faceting 1`] = ` -{ - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, -} -`; - -exports[`Test on faceting Master key: Update faceting at null 1`] = ` -{ - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, -} -`; - -exports[`Test on faceting Master key: Update faceting settings 1`] = ` -{ - "maxValuesPerFacet": 12, - "sortFacetValuesBy": { - "*": "alpha", - "test": "count", - }, -} -`; diff --git a/tests/__snapshots__/settings.test.ts.snap b/tests/__snapshots__/settings.test.ts.snap deleted file mode 100644 index 0f359e2dd..000000000 --- a/tests/__snapshots__/settings.test.ts.snap +++ /dev/null @@ -1,1089 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Test on settings Admin key: Get default settings of an index 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Get default settings of empty index with primary key 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Reset embedders settings 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Reset settings 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Reset settings of empty index 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Update embedders settings 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "embedders": { - "default": { - "documentTemplate": "{% for field in fields %} {{ field.name }}: {{ field.value }} -{% endfor %}", - "model": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", - "source": "huggingFace", - }, - }, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Update searchableAttributes settings on empty index 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "title", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Update searchableAttributes settings on empty index with a primary key 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "title", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Update settings 1`] = ` -{ - "dictionary": [ - "J. K.", - "J. R. R.", - ], - "displayedAttributes": [ - "title", - ], - "distinctAttribute": "title", - "faceting": { - "maxValuesPerFacet": 50, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [ - "title", - ], - "nonSeparatorTokens": [ - "&sep", - "/", - "|", - ], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byAttribute", - "rankingRules": [ - "id:asc", - "typo", - ], - "searchCutoffMs": 1000, - "searchableAttributes": [ - "title", - ], - "separatorTokens": [ - "&sep", - "/", - "|", - ], - "sortableAttributes": [ - "title", - ], - "stopWords": [ - "the", - ], - "synonyms": { - "harry": [ - "potter", - ], - }, - "typoTolerance": { - "disableOnAttributes": [ - "comment", - ], - "disableOnWords": [ - "prince", - ], - "enabled": false, - "minWordSizeForTypos": { - "oneTypo": 1, - "twoTypos": 100, - }, - }, -} -`; - -exports[`Test on settings Admin key: Update settings on empty index with primary key 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": "title", - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "title:asc", - "typo", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [ - "the", - ], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Admin key: Update settings with all null values 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Get default settings of an index 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Get default settings of empty index with primary key 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Reset embedders settings 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Reset settings 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Reset settings of empty index 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Update embedders settings 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "embedders": { - "default": { - "documentTemplate": "{% for field in fields %} {{ field.name }}: {{ field.value }} -{% endfor %}", - "model": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", - "source": "huggingFace", - }, - }, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Update searchableAttributes settings on empty index 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "title", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Update searchableAttributes settings on empty index with a primary key 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "title", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Update settings 1`] = ` -{ - "dictionary": [ - "J. K.", - "J. R. R.", - ], - "displayedAttributes": [ - "title", - ], - "distinctAttribute": "title", - "faceting": { - "maxValuesPerFacet": 50, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [ - "title", - ], - "nonSeparatorTokens": [ - "&sep", - "/", - "|", - ], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byAttribute", - "rankingRules": [ - "id:asc", - "typo", - ], - "searchCutoffMs": 1000, - "searchableAttributes": [ - "title", - ], - "separatorTokens": [ - "&sep", - "/", - "|", - ], - "sortableAttributes": [ - "title", - ], - "stopWords": [ - "the", - ], - "synonyms": { - "harry": [ - "potter", - ], - }, - "typoTolerance": { - "disableOnAttributes": [ - "comment", - ], - "disableOnWords": [ - "prince", - ], - "enabled": false, - "minWordSizeForTypos": { - "oneTypo": 1, - "twoTypos": 100, - }, - }, -} -`; - -exports[`Test on settings Master key: Update settings on empty index with primary key 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": "title", - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "title:asc", - "typo", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [ - "the", - ], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; - -exports[`Test on settings Master key: Update settings with all null values 1`] = ` -{ - "dictionary": [], - "displayedAttributes": [ - "*", - ], - "distinctAttribute": null, - "faceting": { - "maxValuesPerFacet": 100, - "sortFacetValuesBy": { - "*": "alpha", - }, - }, - "filterableAttributes": [], - "nonSeparatorTokens": [], - "pagination": { - "maxTotalHits": 1000, - }, - "proximityPrecision": "byWord", - "rankingRules": [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - ], - "searchCutoffMs": null, - "searchableAttributes": [ - "*", - ], - "separatorTokens": [], - "sortableAttributes": [], - "stopWords": [], - "synonyms": {}, - "typoTolerance": { - "disableOnAttributes": [], - "disableOnWords": [], - "enabled": true, - "minWordSizeForTypos": { - "oneTypo": 5, - "twoTypos": 9, - }, - }, -} -`; From 74b48ab154dec72b02463c1b8df8b92993f694dc Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Wed, 21 Aug 2024 10:30:20 +0300 Subject: [PATCH 12/14] Update all snapshots --- tests/__snapshots__/faceting.test.ts.snap | 75 ++ tests/__snapshots__/settings.test.ts.snap | 1111 +++++++++++++++++++++ 2 files changed, 1186 insertions(+) create mode 100644 tests/__snapshots__/faceting.test.ts.snap create mode 100644 tests/__snapshots__/settings.test.ts.snap diff --git a/tests/__snapshots__/faceting.test.ts.snap b/tests/__snapshots__/faceting.test.ts.snap new file mode 100644 index 000000000..afeb5fc50 --- /dev/null +++ b/tests/__snapshots__/faceting.test.ts.snap @@ -0,0 +1,75 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test on faceting Admin key: Get default faceting object 1`] = ` +{ + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, +} +`; + +exports[`Test on faceting Admin key: Reset faceting 1`] = ` +{ + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, +} +`; + +exports[`Test on faceting Admin key: Update faceting at null 1`] = ` +{ + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, +} +`; + +exports[`Test on faceting Admin key: Update faceting settings 1`] = ` +{ + "maxValuesPerFacet": 12, + "sortFacetValuesBy": { + "*": "alpha", + "test": "count", + }, +} +`; + +exports[`Test on faceting Master key: Get default faceting object 1`] = ` +{ + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, +} +`; + +exports[`Test on faceting Master key: Reset faceting 1`] = ` +{ + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, +} +`; + +exports[`Test on faceting Master key: Update faceting at null 1`] = ` +{ + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, +} +`; + +exports[`Test on faceting Master key: Update faceting settings 1`] = ` +{ + "maxValuesPerFacet": 12, + "sortFacetValuesBy": { + "*": "alpha", + "test": "count", + }, +} +`; diff --git a/tests/__snapshots__/settings.test.ts.snap b/tests/__snapshots__/settings.test.ts.snap new file mode 100644 index 000000000..6e35dca91 --- /dev/null +++ b/tests/__snapshots__/settings.test.ts.snap @@ -0,0 +1,1111 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test on settings Admin key: Get default settings of an index 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Get default settings of empty index with primary key 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Reset embedders settings 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Reset settings 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Reset settings of empty index 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Update embedders settings 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "embedders": { + "default": { + "documentTemplate": "{% for field in fields %} {{ field.name }}: {{ field.value }} +{% endfor %}", + "model": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "source": "huggingFace", + }, + }, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Update searchableAttributes settings on empty index 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "title", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Update searchableAttributes settings on empty index with a primary key 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "title", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Update settings 1`] = ` +{ + "dictionary": [ + "J. K.", + "J. R. R.", + ], + "displayedAttributes": [ + "title", + ], + "distinctAttribute": "title", + "faceting": { + "maxValuesPerFacet": 50, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [ + "title", + ], + "localizedAttributes": null, + "nonSeparatorTokens": [ + "&sep", + "/", + "|", + ], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byAttribute", + "rankingRules": [ + "id:asc", + "typo", + ], + "searchCutoffMs": 1000, + "searchableAttributes": [ + "title", + ], + "separatorTokens": [ + "&sep", + "/", + "|", + ], + "sortableAttributes": [ + "title", + ], + "stopWords": [ + "the", + ], + "synonyms": { + "harry": [ + "potter", + ], + }, + "typoTolerance": { + "disableOnAttributes": [ + "comment", + ], + "disableOnWords": [ + "prince", + ], + "enabled": false, + "minWordSizeForTypos": { + "oneTypo": 1, + "twoTypos": 100, + }, + }, +} +`; + +exports[`Test on settings Admin key: Update settings on empty index with primary key 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": "title", + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "title:asc", + "typo", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [ + "the", + ], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Admin key: Update settings with all null values 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Get default settings of an index 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Get default settings of empty index with primary key 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Reset embedders settings 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Reset settings 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Reset settings of empty index 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Update embedders settings 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "embedders": { + "default": { + "documentTemplate": "{% for field in fields %} {{ field.name }}: {{ field.value }} +{% endfor %}", + "model": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "source": "huggingFace", + }, + }, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Update searchableAttributes settings on empty index 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "title", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Update searchableAttributes settings on empty index with a primary key 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "title", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Update settings 1`] = ` +{ + "dictionary": [ + "J. K.", + "J. R. R.", + ], + "displayedAttributes": [ + "title", + ], + "distinctAttribute": "title", + "faceting": { + "maxValuesPerFacet": 50, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [ + "title", + ], + "localizedAttributes": null, + "nonSeparatorTokens": [ + "&sep", + "/", + "|", + ], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byAttribute", + "rankingRules": [ + "id:asc", + "typo", + ], + "searchCutoffMs": 1000, + "searchableAttributes": [ + "title", + ], + "separatorTokens": [ + "&sep", + "/", + "|", + ], + "sortableAttributes": [ + "title", + ], + "stopWords": [ + "the", + ], + "synonyms": { + "harry": [ + "potter", + ], + }, + "typoTolerance": { + "disableOnAttributes": [ + "comment", + ], + "disableOnWords": [ + "prince", + ], + "enabled": false, + "minWordSizeForTypos": { + "oneTypo": 1, + "twoTypos": 100, + }, + }, +} +`; + +exports[`Test on settings Master key: Update settings on empty index with primary key 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": "title", + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "title:asc", + "typo", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [ + "the", + ], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; + +exports[`Test on settings Master key: Update settings with all null values 1`] = ` +{ + "dictionary": [], + "displayedAttributes": [ + "*", + ], + "distinctAttribute": null, + "faceting": { + "maxValuesPerFacet": 100, + "sortFacetValuesBy": { + "*": "alpha", + }, + }, + "filterableAttributes": [], + "localizedAttributes": null, + "nonSeparatorTokens": [], + "pagination": { + "maxTotalHits": 1000, + }, + "proximityPrecision": "byWord", + "rankingRules": [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + ], + "searchCutoffMs": null, + "searchableAttributes": [ + "*", + ], + "separatorTokens": [], + "sortableAttributes": [], + "stopWords": [], + "synonyms": {}, + "typoTolerance": { + "disableOnAttributes": [], + "disableOnWords": [], + "enabled": true, + "minWordSizeForTypos": { + "oneTypo": 5, + "twoTypos": 9, + }, + }, +} +`; From 30e833f98a0ece09d624829a3ac75bded50e9c75 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:49:45 +0300 Subject: [PATCH 13/14] Add capabilities to update documents by function (#1691) * Add types and function for updateDocumentsByFunction * Add tests * Fixed test * Fixed tests * Add param jsdoc * Fix style * Fix test, make tests more consistent --- src/indexes.ts | 22 ++++++ src/types/types.ts | 6 ++ tests/documents.test.ts | 105 +++++++++++++++++++++++++- tests/utils/meilisearch-test-utils.ts | 4 +- 4 files changed, 134 insertions(+), 3 deletions(-) diff --git a/src/indexes.ts b/src/indexes.ts index 048812d27..af7f36f78 100644 --- a/src/indexes.ts +++ b/src/indexes.ts @@ -55,6 +55,7 @@ import { SearchCutoffMs, SearchSimilarDocumentsParams, LocalizedAttributes, + UpdateDocumentsByFunctionOptions, } from './types'; import { removeUndefinedFromObject } from './utils'; import { HttpRequests } from './http-requests'; @@ -630,6 +631,27 @@ class Index = Record> { return task; } + /** + * This is an EXPERIMENTAL feature, which may break without a major version. + * It's available after Meilisearch v1.10. + * + * More info about the feature: + * https://github.com/orgs/meilisearch/discussions/762 More info about + * experimental features in general: + * https://www.meilisearch.com/docs/reference/api/experimental-features + * + * @param options - Object containing the function string and related options + * @returns Promise containing an EnqueuedTask + */ + async updateDocumentsByFunction( + options: UpdateDocumentsByFunctionOptions, + ): Promise { + const url = `indexes/${this.uid}/documents/edit`; + const task = await this.httpRequest.post(url, options); + + return new EnqueuedTask(task); + } + /// /// SETTINGS /// diff --git a/src/types/types.ts b/src/types/types.ts index 338af5d66..9c08bc4ad 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -331,6 +331,12 @@ export type DocumentsDeletionQuery = { export type DocumentsIds = string[] | number[]; +export type UpdateDocumentsByFunctionOptions = { + function: string; + filter?: string | string[]; + context?: Record; +}; + /* ** Settings */ diff --git a/tests/documents.test.ts b/tests/documents.test.ts index 8cb232e26..ac52be52c 100644 --- a/tests/documents.test.ts +++ b/tests/documents.test.ts @@ -144,7 +144,12 @@ describe('Documents tests', () => { test(`${permission} key: Get documents with filters`, async () => { const client = await getClient(permission); - await client.index(indexPk.uid).updateFilterableAttributes(['id']); + + const { taskUid: updateFilterableAttributesTaskUid } = await client + .index(indexPk.uid) + .updateFilterableAttributes(['id']); + await client.waitForTask(updateFilterableAttributesTaskUid); + const { taskUid } = await client .index(indexPk.uid) .addDocuments(dataset); @@ -780,6 +785,42 @@ Hint: It might not be working because maybe you're not up to date with the Meili expect(index.primaryKey).toEqual(null); expect(task.status).toEqual('failed'); }); + + test(`${permission} key: test updateDocumentsByFunction`, async () => { + const client = await getClient(permission); + const index = client.index<(typeof dataset)[number]>(indexPk.uid); + const adminKey = await getKey('Admin'); + + const { taskUid: updateFilterableAttributesTaskUid } = + await index.updateFilterableAttributes(['id']); + await client.waitForTask(updateFilterableAttributesTaskUid); + + await fetch(`${HOST}/experimental-features`, { + body: JSON.stringify({ editDocumentsByFunction: true }), + headers: { + Authorization: `Bearer ${adminKey}`, + 'Content-Type': 'application/json', + }, + method: 'PATCH', + }); + + const { taskUid: addDocumentsTaskUid } = + await index.addDocuments(dataset); + await index.waitForTask(addDocumentsTaskUid); + + const { taskUid: updateDocumentsByFunctionTaskUid } = + await index.updateDocumentsByFunction({ + context: { ctx: 'Harry' }, + filter: 'id = 4', + function: 'doc.comment = `Yer a wizard, ${context.ctx}!`', + }); + + await client.waitForTask(updateDocumentsByFunctionTaskUid); + + const doc = await index.getDocument(4); + + expect(doc).toHaveProperty('comment', 'Yer a wizard, Harry!'); + }); }, ); @@ -831,6 +872,24 @@ Hint: It might not be working because maybe you're not up to date with the Meili client.index(indexPk.uid).deleteAllDocuments(), ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INVALID_API_KEY); }); + + test(`${permission} key: Try updateDocumentsByFunction and be denied`, async () => { + const client = await getClient(permission); + const adminKey = await getKey('Admin'); + + await fetch(`${HOST}/experimental-features`, { + body: JSON.stringify({ editDocumentsByFunction: true }), + headers: { + Authorization: `Bearer ${adminKey}`, + 'Content-Type': 'application/json', + }, + method: 'PATCH', + }); + + await expect( + client.index(indexPk.uid).updateDocumentsByFunction({ function: '' }), + ).rejects.toHaveProperty('cause.code', ErrorStatusCode.INVALID_API_KEY); + }); }, ); @@ -900,6 +959,27 @@ Hint: It might not be working because maybe you're not up to date with the Meili ErrorStatusCode.MISSING_AUTHORIZATION_HEADER, ); }); + + test(`${permission} key: Try updateDocumentsByFunction and be denied`, async () => { + const client = await getClient(permission); + const adminKey = await getKey('Admin'); + + await fetch(`${HOST}/experimental-features`, { + body: JSON.stringify({ editDocumentsByFunction: true }), + headers: { + Authorization: `Bearer ${adminKey}`, + 'Content-Type': 'application/json', + }, + method: 'PATCH', + }); + + await expect( + client.index(indexPk.uid).updateDocumentsByFunction({ function: '' }), + ).rejects.toHaveProperty( + 'cause.code', + ErrorStatusCode.MISSING_AUTHORIZATION_HEADER, + ); + }); }, ); @@ -991,5 +1071,28 @@ Hint: It might not be working because maybe you're not up to date with the Meili `Request to ${strippedHost}/${route} has failed`, ); }); + + test(`Test updateDocumentsByFunction route`, async () => { + const route = `indexes/${indexPk.uid}/documents/edit`; + const client = new MeiliSearch({ host }); + const strippedHost = trailing ? host.slice(0, -1) : host; + const adminKey = await getKey('Admin'); + + await fetch(`${HOST}/experimental-features`, { + body: JSON.stringify({ editDocumentsByFunction: true }), + headers: { + Authorization: `Bearer ${adminKey}`, + 'Content-Type': 'application/json', + }, + method: 'PATCH', + }); + + await expect( + client.index(indexPk.uid).updateDocumentsByFunction({ function: '' }), + ).rejects.toHaveProperty( + 'message', + `Request to ${strippedHost}/${route} has failed`, + ); + }); }); }); diff --git a/tests/utils/meilisearch-test-utils.ts b/tests/utils/meilisearch-test-utils.ts index d1313328c..e9d06c747 100644 --- a/tests/utils/meilisearch-test-utils.ts +++ b/tests/utils/meilisearch-test-utils.ts @@ -80,7 +80,7 @@ const clearAllIndexes = async (config: Config): Promise => { const { results } = await client.getRawIndexes(); const indexes = results.map((elem) => elem.uid); - const taskIds = []; + const taskIds: number[] = []; for (const indexUid of indexes) { const { taskUid } = await client.index(indexUid).delete(); taskIds.push(taskUid); @@ -144,7 +144,7 @@ const datasetWithNests = [ { id: 7, title: "The Hitchhiker's Guide to the Galaxy" }, ]; -const dataset = [ +const dataset: Array<{ id: number; title: string; comment?: string }> = [ { id: 123, title: 'Pride and Prejudice', comment: 'A great book' }, { id: 456, title: 'Le Petit Prince', comment: 'A french book' }, { id: 2, title: 'Le Rouge et le Noir', comment: 'Another french book' }, From 3a8608b56020166779a6c3baf051a7e436dfaad5 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:52:51 +0300 Subject: [PATCH 14/14] Add federated search parameters (#1689) * Remove cross-fetch dependency * Add types for federation feature * Add test, minor changes to types * Revert "Remove cross-fetch dependency" This reverts commit 899d2a7b78e868f4a5d33be1be45837269b0ea34. * Revert generic name back to T for consistency * Revert rest of the generics to T * Remove focus from test * Revert changes to test-utils getClient * Add back accidentally removed type from test utils --- src/clients/client.ts | 18 +++++++++++++----- src/types/types.ts | 31 ++++++++++++++++++++++++------- tests/search.test.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/clients/client.ts b/src/clients/client.ts index ac031601a..520cdf687 100644 --- a/src/clients/client.ts +++ b/src/clients/client.ts @@ -5,8 +5,6 @@ * Copyright: 2019, MeiliSearch */ -'use strict'; - import { Index } from '../indexes'; import { KeyCreation, @@ -34,6 +32,8 @@ import { DeleteTasksQuery, MultiSearchParams, MultiSearchResponse, + SearchResponse, + FederatedMultiSearchParams, } from '../types'; import { HttpRequests } from '../http-requests'; import { TaskClient, Task } from '../task'; @@ -216,10 +216,18 @@ class Client { * @param config - Additional request configuration options * @returns Promise containing the search responses */ - async multiSearch = Record>( - queries?: MultiSearchParams, + multiSearch = Record>( + queries: MultiSearchParams, + config?: Partial, + ): Promise>; + multiSearch = Record>( + queries: FederatedMultiSearchParams, + config?: Partial, + ): Promise>; + async multiSearch = Record>( + queries: MultiSearchParams | FederatedMultiSearchParams, config?: Partial, - ): Promise> { + ): Promise | SearchResponse> { const url = `multi-search`; return await this.httpRequest.post(url, queries, undefined, config); diff --git a/src/types/types.ts b/src/types/types.ts index 9c08bc4ad..768c429da 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -159,11 +159,21 @@ export type SearchRequestGET = Pagination & locales?: Locale[]; }; +export type FederationOptions = { weight: number }; +export type MultiSearchFederation = { limit?: number; offset?: number }; + export type MultiSearchQuery = SearchParams & { indexUid: string }; +export type MultiSearchQueryWithFederation = MultiSearchQuery & { + federationOptions?: FederationOptions; +}; export type MultiSearchParams = { queries: MultiSearchQuery[]; }; +export type FederatedMultiSearchParams = { + federation: MultiSearchFederation; + queries: MultiSearchQueryWithFederation[]; +}; export type CategoriesDistribution = { [category: string]: number; @@ -175,13 +185,6 @@ export type MatchesPosition = Partial< Record> >; -export type Hit> = T & { - _formatted?: Partial; - _matchesPosition?: MatchesPosition; - _rankingScore?: number; - _rankingScoreDetails?: RankingScoreDetails; -}; - export type RankingScoreDetails = { words?: { order: number; @@ -213,6 +216,20 @@ export type RankingScoreDetails = { [key: string]: Record | undefined; }; +export type FederationDetails = { + indexUid: string; + queriesPosition: number; + weightedRankingScore: number; +}; + +export type Hit> = T & { + _formatted?: Partial; + _matchesPosition?: MatchesPosition; + _rankingScore?: number; + _rankingScoreDetails?: RankingScoreDetails; + _federation?: FederationDetails; +}; + export type Hits> = Array>; export type FacetStat = { min: number; max: number }; diff --git a/tests/search.test.ts b/tests/search.test.ts index 8ff3a173c..e1d66f64f 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -143,6 +143,48 @@ describe.each([ expect(response.results[0].hits[0].title).toEqual('Le Petit Prince'); }); + test(`${permission} key: Multi index search with federation`, async () => { + const client = await getClient(permission); + + const response1 = await client.multiSearch< + Books | { id: number; asd: string } + >({ + federation: {}, + queries: [ + { indexUid: index.uid, q: '456', attributesToSearchOn: ['id'] }, + { + indexUid: index.uid, + q: '1344', + federationOptions: { weight: 0.9 }, + attributesToSearchOn: ['id'], + }, + ], + }); + + expect(response1).toHaveProperty('hits'); + expect(Array.isArray(response1.hits)).toBe(true); + expect(response1.hits.length).toEqual(2); + expect(response1.hits[0].id).toEqual(456); + + const response2 = await client.multiSearch({ + federation: {}, + queries: [ + { + indexUid: index.uid, + q: '456', + federationOptions: { weight: 0.9 }, + attributesToSearchOn: ['id'], + }, + { indexUid: index.uid, q: '1344', attributesToSearchOn: ['id'] }, + ], + }); + + expect(response2).toHaveProperty('hits'); + expect(Array.isArray(response2.hits)).toBe(true); + expect(response2.hits.length).toEqual(2); + expect(response2.hits[0].id).toEqual(1344); + }); + test(`${permission} key: Basic search`, async () => { const client = await getClient(permission); const response = await client.index(index.uid).search('prince', {});