From 2d4f6ffe8416af4a65e4b38d648223b75e53d29a Mon Sep 17 00:00:00 2001 From: Hugues BUREAU Date: Fri, 17 Jan 2025 16:07:20 +0100 Subject: [PATCH 1/7] feat: handle per index meilisearch params --- .../search-params-adapter.ts | 52 +++++++++++++------ .../instant-meilisearch/src/types/types.ts | 6 ++- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts index 0dc2930e..c0a5395e 100644 --- a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts +++ b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts @@ -4,6 +4,7 @@ import type { PaginationState, MeiliSearchMultiSearchParams, Mutable, + OverridableMeiliSearchSearchParameters, } from '../../types' import { adaptGeoSearch } from './geo-rules-adapter' @@ -105,6 +106,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addAttributesToCrop() { const value = + overrideParams?.[indexUid]?.attributesToCrop ?? overrideParams?.attributesToCrop ?? >attributesToSnippet if (value !== undefined) { @@ -112,13 +114,17 @@ export function MeiliParamsCreator(searchContext: SearchContext) { } }, addCropLength() { - const value = overrideParams?.cropLength + const value = + overrideParams?.[indexUid]?.cropLength ?? overrideParams?.cropLength if (value !== undefined) { meiliSearchParams.cropLength = value } }, addCropMarker() { - const value = overrideParams?.cropMarker ?? snippetEllipsisText + const value = + overrideParams?.[indexUid]?.cropMarker ?? + overrideParams?.cropMarker ?? + snippetEllipsisText if (value !== undefined) { meiliSearchParams.cropMarker = value } @@ -130,6 +136,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addAttributesToRetrieve() { const value = + overrideParams?.[indexUid]?.attributesToRetrieve ?? overrideParams?.attributesToRetrieve ?? >attributesToRetrieve if (value !== undefined) { @@ -137,18 +144,21 @@ export function MeiliParamsCreator(searchContext: SearchContext) { } }, addAttributesToHighlight() { - meiliSearchParams.attributesToHighlight = + meiliSearchParams.attributesToHighlight = overrideParams?.[indexUid] + ?.attributesToHighlight ?? overrideParams?.attributesToHighlight ?? - >attributesToHighlight ?? ['*'] + >attributesToHighlight ?? ['*'] }, addPreTag() { meiliSearchParams.highlightPreTag = + overrideParams?.[indexUid]?.highlightPreTag ?? overrideParams?.highlightPreTag ?? highlightPreTag ?? '__ais-highlight__' }, addPostTag() { meiliSearchParams.highlightPostTag = + overrideParams?.[indexUid]?.highlightPostTag ?? overrideParams?.highlightPostTag ?? highlightPostTag ?? '__/ais-highlight__' @@ -204,54 +214,64 @@ export function MeiliParamsCreator(searchContext: SearchContext) { } }, addShowMatchesPosition() { - const value = overrideParams?.showMatchesPosition + const value = + overrideParams?.[indexUid]?.showMatchesPosition ?? + overrideParams?.showMatchesPosition if (value !== undefined) { meiliSearchParams.showMatchesPosition = value } }, addMatchingStrategy() { - const value = overrideParams?.matchingStrategy + const value = + overrideParams?.[indexUid]?.matchingStrategy ?? + overrideParams?.matchingStrategy if (value !== undefined) { meiliSearchParams.matchingStrategy = value } }, addShowRankingScore() { - const value = overrideParams?.showRankingScore + const value = + overrideParams?.[indexUid]?.showRankingScore ?? + overrideParams?.showRankingScore if (value !== undefined) { meiliSearchParams.showRankingScore = value } }, addAttributesToSearchOn() { const value = - overrideParams?.attributesToSearchOn !== undefined - ? overrideParams.attributesToSearchOn - : >( - restrictSearchableAttributes - ) + overrideParams?.[indexUid]?.attributesToSearchOn ?? + overrideParams?.attributesToSearchOn ?? + >( + restrictSearchableAttributes + ) + if (value !== undefined) { meiliSearchParams.attributesToSearchOn = value } }, addHybridSearch() { - const value = overrideParams?.hybrid + const value = overrideParams?.[indexUid]?.hybrid ?? overrideParams?.hybrid if (value !== undefined) { meiliSearchParams.hybrid = value } }, addVector() { - const value = overrideParams?.vector + const value = overrideParams?.[indexUid]?.vector ?? overrideParams?.vector if (value !== undefined) { meiliSearchParams.vector = value } }, addDistinct() { - const value = overrideParams?.distinct + const value = + overrideParams?.[indexUid]?.distinct ?? overrideParams?.distinct if (value !== undefined) { meiliSearchParams.distinct = value } }, addRankingScoreThreshold() { - const value = overrideParams?.rankingScoreThreshold + const value = + overrideParams?.[indexUid]?.rankingScoreThreshold ?? + overrideParams?.rankingScoreThreshold if (value !== undefined) { meiliSearchParams.rankingScoreThreshold = value } diff --git a/packages/instant-meilisearch/src/types/types.ts b/packages/instant-meilisearch/src/types/types.ts index 26eba4cb..65f4ac0f 100644 --- a/packages/instant-meilisearch/src/types/types.ts +++ b/packages/instant-meilisearch/src/types/types.ts @@ -41,7 +41,7 @@ export type InstantSearchParams = NonNullable< AlgoliaMultipleQueriesQuery['params'] > -export type OverridableMeiliSearchSearchParameters = Pick< +type BaseMeiliSearchSearchParameters = Pick< MeiliSearchMultiSearchParams, | 'attributesToCrop' | 'attributesToRetrieve' @@ -60,6 +60,10 @@ export type OverridableMeiliSearchSearchParameters = Pick< | 'vector' > +export type OverridableMeiliSearchSearchParameters = { + [indexName: string]: BaseMeiliSearchSearchParameters +} & BaseMeiliSearchSearchParameters + type BaseInstantMeiliSearchOptions = { placeholderSearch?: boolean primaryKey?: string From 1b6b99e13cad1db6bbbb90709eb156745c350042 Mon Sep 17 00:00:00 2001 From: Hugues BUREAU Date: Fri, 17 Jan 2025 17:00:35 +0100 Subject: [PATCH 2/7] fix: typescript error --- .../search-params-adapter.ts | 38 +++++++++++-------- .../instant-meilisearch/src/types/types.ts | 12 ++++-- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts index c0a5395e..fe84de9c 100644 --- a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts +++ b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts @@ -106,7 +106,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addAttributesToCrop() { const value = - overrideParams?.[indexUid]?.attributesToCrop ?? + overrideParams?.indexesOverrides?.[indexUid]?.attributesToCrop ?? overrideParams?.attributesToCrop ?? >attributesToSnippet if (value !== undefined) { @@ -115,14 +115,15 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addCropLength() { const value = - overrideParams?.[indexUid]?.cropLength ?? overrideParams?.cropLength + overrideParams?.indexesOverrides?.[indexUid]?.cropLength ?? + overrideParams?.cropLength if (value !== undefined) { meiliSearchParams.cropLength = value } }, addCropMarker() { const value = - overrideParams?.[indexUid]?.cropMarker ?? + overrideParams?.indexesOverrides?.[indexUid]?.cropMarker ?? overrideParams?.cropMarker ?? snippetEllipsisText if (value !== undefined) { @@ -136,7 +137,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addAttributesToRetrieve() { const value = - overrideParams?.[indexUid]?.attributesToRetrieve ?? + overrideParams?.indexesOverrides?.[indexUid]?.attributesToRetrieve ?? overrideParams?.attributesToRetrieve ?? >attributesToRetrieve if (value !== undefined) { @@ -144,21 +145,21 @@ export function MeiliParamsCreator(searchContext: SearchContext) { } }, addAttributesToHighlight() { - meiliSearchParams.attributesToHighlight = overrideParams?.[indexUid] - ?.attributesToHighlight ?? + meiliSearchParams.attributesToHighlight = overrideParams + ?.indexesOverrides?.[indexUid]?.attributesToHighlight ?? overrideParams?.attributesToHighlight ?? >attributesToHighlight ?? ['*'] }, addPreTag() { meiliSearchParams.highlightPreTag = - overrideParams?.[indexUid]?.highlightPreTag ?? + overrideParams?.indexesOverrides?.[indexUid]?.highlightPreTag ?? overrideParams?.highlightPreTag ?? highlightPreTag ?? '__ais-highlight__' }, addPostTag() { meiliSearchParams.highlightPostTag = - overrideParams?.[indexUid]?.highlightPostTag ?? + overrideParams?.indexesOverrides?.[indexUid]?.highlightPostTag ?? overrideParams?.highlightPostTag ?? highlightPostTag ?? '__/ais-highlight__' @@ -215,7 +216,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addShowMatchesPosition() { const value = - overrideParams?.[indexUid]?.showMatchesPosition ?? + overrideParams?.indexesOverrides?.[indexUid]?.showMatchesPosition ?? overrideParams?.showMatchesPosition if (value !== undefined) { meiliSearchParams.showMatchesPosition = value @@ -223,7 +224,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addMatchingStrategy() { const value = - overrideParams?.[indexUid]?.matchingStrategy ?? + overrideParams?.indexesOverrides?.[indexUid]?.matchingStrategy ?? overrideParams?.matchingStrategy if (value !== undefined) { meiliSearchParams.matchingStrategy = value @@ -231,7 +232,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addShowRankingScore() { const value = - overrideParams?.[indexUid]?.showRankingScore ?? + overrideParams?.indexesOverrides?.[indexUid]?.showRankingScore ?? overrideParams?.showRankingScore if (value !== undefined) { meiliSearchParams.showRankingScore = value @@ -239,7 +240,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addAttributesToSearchOn() { const value = - overrideParams?.[indexUid]?.attributesToSearchOn ?? + overrideParams?.indexesOverrides?.[indexUid]?.attributesToSearchOn ?? overrideParams?.attributesToSearchOn ?? >( restrictSearchableAttributes @@ -250,27 +251,32 @@ export function MeiliParamsCreator(searchContext: SearchContext) { } }, addHybridSearch() { - const value = overrideParams?.[indexUid]?.hybrid ?? overrideParams?.hybrid + const value = + overrideParams?.indexesOverrides?.[indexUid]?.hybrid ?? + overrideParams?.hybrid if (value !== undefined) { meiliSearchParams.hybrid = value } }, addVector() { - const value = overrideParams?.[indexUid]?.vector ?? overrideParams?.vector + const value = + overrideParams?.indexesOverrides?.[indexUid]?.vector ?? + overrideParams?.vector if (value !== undefined) { meiliSearchParams.vector = value } }, addDistinct() { const value = - overrideParams?.[indexUid]?.distinct ?? overrideParams?.distinct + overrideParams?.indexesOverrides?.[indexUid]?.distinct ?? + overrideParams?.distinct if (value !== undefined) { meiliSearchParams.distinct = value } }, addRankingScoreThreshold() { const value = - overrideParams?.[indexUid]?.rankingScoreThreshold ?? + overrideParams?.indexesOverrides?.[indexUid]?.rankingScoreThreshold ?? overrideParams?.rankingScoreThreshold if (value !== undefined) { meiliSearchParams.rankingScoreThreshold = value diff --git a/packages/instant-meilisearch/src/types/types.ts b/packages/instant-meilisearch/src/types/types.ts index 65f4ac0f..612063aa 100644 --- a/packages/instant-meilisearch/src/types/types.ts +++ b/packages/instant-meilisearch/src/types/types.ts @@ -41,7 +41,7 @@ export type InstantSearchParams = NonNullable< AlgoliaMultipleQueriesQuery['params'] > -type BaseMeiliSearchSearchParameters = Pick< +type BaseOverridableMeiliSearchSearchParameters = Pick< MeiliSearchMultiSearchParams, | 'attributesToCrop' | 'attributesToRetrieve' @@ -60,9 +60,13 @@ type BaseMeiliSearchSearchParameters = Pick< | 'vector' > -export type OverridableMeiliSearchSearchParameters = { - [indexName: string]: BaseMeiliSearchSearchParameters -} & BaseMeiliSearchSearchParameters +export type OverridableMeiliSearchSearchParameters = + BaseOverridableMeiliSearchSearchParameters & { + indexesOverrides?: Record< + string, + BaseOverridableMeiliSearchSearchParameters + > + } type BaseInstantMeiliSearchOptions = { placeholderSearch?: boolean From c70f6bf193f59c015310ded9ac14f56443e34d2b Mon Sep 17 00:00:00 2001 From: Hugues BUREAU Date: Fri, 17 Jan 2025 17:19:02 +0100 Subject: [PATCH 3/7] test: make sure adding an index properly overrides base meilisearchParams --- .../__tests__/search-params.test.ts | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/packages/instant-meilisearch/src/adapter/search-request-adapter/__tests__/search-params.test.ts b/packages/instant-meilisearch/src/adapter/search-request-adapter/__tests__/search-params.test.ts index 57eae476..2facecdc 100644 --- a/packages/instant-meilisearch/src/adapter/search-request-adapter/__tests__/search-params.test.ts +++ b/packages/instant-meilisearch/src/adapter/search-request-adapter/__tests__/search-params.test.ts @@ -79,6 +79,41 @@ describe('Parameters adapter', () => { ) }) + test('adapting a searchContext with overridden Meilisearch parameters for a specific index', () => { + const meiliSearchParams: OverridableMeiliSearchSearchParameters = { + attributesToHighlight: ['movies', 'genres'], + highlightPreTag: '', + highlightPostTag: '', + matchingStrategy: MatchingStrategies.ALL, + indexesOverrides: { + test: { + attributesToHighlight: ['release_date'], + highlightPreTag: '', + highlightPostTag: '', + matchingStrategy: MatchingStrategies.LAST, + }, + }, + } + + const searchParams = adaptSearchParams({ + ...DEFAULT_CONTEXT, + meiliSearchParams, + }) + + expect(searchParams.attributesToHighlight).toEqual( + meiliSearchParams.indexesOverrides?.test?.attributesToHighlight + ) + expect(searchParams.highlightPreTag).toEqual( + meiliSearchParams.indexesOverrides?.test?.highlightPreTag + ) + expect(searchParams.highlightPostTag).toEqual( + meiliSearchParams.indexesOverrides?.test?.highlightPostTag + ) + expect(searchParams.matchingStrategy).toEqual( + meiliSearchParams.indexesOverrides?.test?.matchingStrategy + ) + }) + test('hybrid search configuration can be set via search parameters', () => { const hybridSearchConfig = { semanticRatio: 0, @@ -95,6 +130,29 @@ describe('Parameters adapter', () => { expect(searchParams.hybrid).toBe(hybridSearchConfig) }) + test('hybrid search configuration can be set via search parameters for a specific index', () => { + const hybridSearchConfig = { + semanticRatio: 0, + embedder: 'default', + } + const specificHybridSearchConfig = { + semanticRatio: 10, + embedder: 'default', + } + + const searchParams = adaptSearchParams({ + ...DEFAULT_CONTEXT, + meiliSearchParams: { + hybrid: hybridSearchConfig, + indexesOverrides: { + test: { hybrid: specificHybridSearchConfig }, + }, + }, + }) + + expect(searchParams.hybrid).toBe(specificHybridSearchConfig) + }) + test('vector can be set via search parameters', () => { const vector = [0, 1, 2] @@ -107,6 +165,20 @@ describe('Parameters adapter', () => { expect(searchParams.vector).toBe(vector) }) + test('vector can be set via search parameters for a specific index', () => { + const vector = [0, 1, 2] + const indexVector = [3, 4, 5] + + const searchParams = adaptSearchParams({ + ...DEFAULT_CONTEXT, + meiliSearchParams: { + vector, + indexesOverrides: { test: { vector: indexVector } }, + }, + }) + + expect(searchParams.vector).toBe(indexVector) + }) test('ranking score threshold can be set via search parameters', () => { const rankingScoreThreshold = 0.974 @@ -121,6 +193,23 @@ describe('Parameters adapter', () => { expect(searchParams.rankingScoreThreshold).toBe(rankingScoreThreshold) }) + test('ranking score threshold can be set via search parameters for a specific index', () => { + const rankingScoreThreshold = 0.974 + const indexRankingScoreThreshold = 0.567 + + const searchParams = adaptSearchParams({ + ...DEFAULT_CONTEXT, + meiliSearchParams: { + rankingScoreThreshold, + indexesOverrides: { + test: { rankingScoreThreshold: indexRankingScoreThreshold }, + }, + }, + }) + + expect(searchParams.rankingScoreThreshold).toBe(indexRankingScoreThreshold) + }) + test('distinct search configuration can be set via search parameters', () => { const distinctSearchConfig = 'title' @@ -133,6 +222,23 @@ describe('Parameters adapter', () => { expect(searchParams.distinct).toBe(distinctSearchConfig) }) + + test('distinct search configuration can be set via search parameters for a specific', () => { + const distinctSearchConfig = 'title' + const indexDistinctSearchConfig = 'name' + + const searchParams = adaptSearchParams({ + ...DEFAULT_CONTEXT, + meiliSearchParams: { + distinct: distinctSearchConfig, + indexesOverrides: { + test: { distinct: indexDistinctSearchConfig }, + }, + }, + }) + + expect(searchParams.distinct).toBe(indexDistinctSearchConfig) + }) }) describe('Geo filter adapter', () => { From 188011f45cfda29722366e13e1dde80f0fc14799 Mon Sep 17 00:00:00 2001 From: Hugues BUREAU Date: Fri, 17 Jan 2025 17:23:39 +0100 Subject: [PATCH 4/7] test: override client meilisearchParams in context --- .../overridden-meilisearch-parameters.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/instant-meilisearch/__tests__/overridden-meilisearch-parameters.test.ts b/packages/instant-meilisearch/__tests__/overridden-meilisearch-parameters.test.ts index 7ad9e1f2..e5c3eeca 100644 --- a/packages/instant-meilisearch/__tests__/overridden-meilisearch-parameters.test.ts +++ b/packages/instant-meilisearch/__tests__/overridden-meilisearch-parameters.test.ts @@ -50,5 +50,18 @@ describe('InstantMeiliSearch overridden parameters', () => { expect(secondHits[0]._highlightResult?.overview?.value).toContain( 'While racing to a boxing match' ) + + setMeiliSearchParams({ + indexesOverrides: { + movies: { highlightPreTag: '', highlightPostTag: '' }, + }, + }) + const thirdResponse = await searchClient.search(queryParams) + + const thirdHits = thirdResponse.results[0].hits + expect(thirdHits.length).toEqual(1) + expect(thirdHits[0]._highlightResult?.overview?.value).toContain( + 'While racing to a boxing match' + ) }) }) From 12d9e195c53658ff5ca6617c97c2b4bd48b61a68 Mon Sep 17 00:00:00 2001 From: Hugues BUREAU Date: Fri, 17 Jan 2025 17:24:32 +0100 Subject: [PATCH 5/7] lint: remove unused import --- .../src/adapter/search-request-adapter/search-params-adapter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts index fe84de9c..5e223f5b 100644 --- a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts +++ b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts @@ -4,7 +4,6 @@ import type { PaginationState, MeiliSearchMultiSearchParams, Mutable, - OverridableMeiliSearchSearchParameters, } from '../../types' import { adaptGeoSearch } from './geo-rules-adapter' From 7c40e6e4c0ceed0bc68352b7c8175db5e5f35f6c Mon Sep 17 00:00:00 2001 From: Hugues BUREAU Date: Fri, 17 Jan 2025 17:28:39 +0100 Subject: [PATCH 6/7] docs: update README with example for indexOverrides --- packages/instant-meilisearch/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/instant-meilisearch/README.md b/packages/instant-meilisearch/README.md index 4a180fb5..1ac90dcd 100644 --- a/packages/instant-meilisearch/README.md +++ b/packages/instant-meilisearch/README.md @@ -296,7 +296,28 @@ instantMeiliSearch( } ) ``` +When using multi search, meilisearchParams can be overriden for specific indexes : +```js +instantMeiliSearch( + // ... + { + meiliSearchParams: { + // All indexes will highlight overview + attributesToHighlight: ['overview'], + highlightPreTag: '', + highlightPostTag: '', + attributesToSearchOn: ['overview'], + indexesOverrides: { + movies: { + // Only title will be highlighted for hits in movies + attributesToHighlight: ['title'] + } + } + }, + } +) +``` ### Modify Meilisearch search parameters `instantMeiliSearch` returns an instance with two properties on it, one of them being `setMeiliSearchParams`. From af746f30ebaa4555db729331a1ca894654529ffc Mon Sep 17 00:00:00 2001 From: Hugues BUREAU Date: Tue, 21 Jan 2025 16:33:35 +0100 Subject: [PATCH 7/7] feat: make facets and filters overridable --- .../search-request-adapter/search-params-adapter.ts | 10 ++++++++-- packages/instant-meilisearch/src/types/types.ts | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts index 5e223f5b..7600a673 100644 --- a/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts +++ b/packages/instant-meilisearch/src/adapter/search-request-adapter/search-params-adapter.ts @@ -96,7 +96,10 @@ export function MeiliParamsCreator(searchContext: SearchContext) { meiliSearchParams.q = query }, addFacets() { - const value = >facets + const value = + overrideParams?.indexesOverrides?.[indexUid]?.facets ?? + overrideParams?.facets ?? + >facets if (value !== undefined) { // despite Instantsearch.js typing it as `string[]`, // it still can send `string` @@ -131,7 +134,10 @@ export function MeiliParamsCreator(searchContext: SearchContext) { }, addFilters() { if (meilisearchFilters.length) { - meiliSearchParams.filter = meilisearchFilters + meiliSearchParams.filter = + overrideParams?.indexesOverrides?.[indexUid]?.filter ?? + overrideParams?.filter ?? + meilisearchFilters } }, addAttributesToRetrieve() { diff --git a/packages/instant-meilisearch/src/types/types.ts b/packages/instant-meilisearch/src/types/types.ts index 612063aa..13cd4227 100644 --- a/packages/instant-meilisearch/src/types/types.ts +++ b/packages/instant-meilisearch/src/types/types.ts @@ -43,6 +43,10 @@ export type InstantSearchParams = NonNullable< type BaseOverridableMeiliSearchSearchParameters = Pick< MeiliSearchMultiSearchParams, + | 'sort' + | 'hitsPerPage' + | 'filter' + | 'facets' | 'attributesToCrop' | 'attributesToRetrieve' | 'attributesToSearchOn'