From debec66383a957425aec651955fa528e92009084 Mon Sep 17 00:00:00 2001 From: Charlotte Vermandel Date: Thu, 1 Sep 2022 15:07:57 +0200 Subject: [PATCH 1/3] Add matchingStrategy in readme --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 0c11a027..05f34b88 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ const searchClient = instantMeiliSearch( - [`finitePagination`](#finite-pagination): Used to work with the [`pagination`](#-pagination) widget (default: `false`) . - [`primaryKey`](#primary-key): Specify the primary key of your documents (default `undefined`). - [`keepZeroFacets`](#keep-zero-facets): Show the facets value even when they have 0 matches (default `false`). +- [`matchingStrategy`](#matching-strategy): Determine the search strategy on words matching (default `last`). The options are added as the third parameter of the `instantMeilisearch` function. @@ -170,6 +171,21 @@ genres: { keepZeroFacets : true } // default: false ``` +### Matching strategy + +`matchingStrategy` gives you the possibility to chose how Meilisearch should handle the presence of multiple query words. + +For example, if your query is `Hello world` by default Meilisearch returns documents containing either both `Hello` and `world` or documents that only contain `hello`. This is the `last` strategy, where words are stripped from the right. +The other strategy is `all`, where both `hello` and `worlds` **must** be present in a document for it to be returned. + +// TODO: add documentation link + +```js +{ + optionalWords: 'all' // default last +} +``` + ## 🪡 Example with InstantSearch The open-source [InstantSearch](https://www.algolia.com/doc/api-reference/widgets/js/) library powered by Algolia provides all the front-end tools you need to highly customize your search bar environment. From bba3db8a0cf83284d59942aa0d57d07be1daec74 Mon Sep 17 00:00:00 2001 From: Charlotte Vermandel Date: Thu, 1 Sep 2022 15:31:44 +0200 Subject: [PATCH 2/3] Add matching strategy as an option --- README.md | 2 +- .../__tests__/search-params.tests.ts | 23 +++++++++---------- .../search-params-adapter.ts | 7 ++++++ src/contexts/search-context.ts | 2 ++ src/types/types.ts | 7 ++++++ 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 05f34b88..93904139 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ The other strategy is `all`, where both `hello` and `worlds` **must** be present ```js { - optionalWords: 'all' // default last + matchingStrategy: 'all' // default last } ``` diff --git a/src/adapter/search-request-adapter/__tests__/search-params.tests.ts b/src/adapter/search-request-adapter/__tests__/search-params.tests.ts index 067cd4e7..2feaf5e1 100644 --- a/src/adapter/search-request-adapter/__tests__/search-params.tests.ts +++ b/src/adapter/search-request-adapter/__tests__/search-params.tests.ts @@ -1,16 +1,17 @@ import { adaptSearchParams } from '../search-params-adapter' +import { MatchingStrategies } from '../../../types' const DEFAULT_CONTEXT = { indexUid: 'test', pagination: { paginationTotalHits: 20, page: 0, hitsPerPage: 6 }, defaultFacetDistribution: {}, + finitePagination: false, } describe('Parameters adapter', () => { test('adapting a basic searchContext ', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, - finitePagination: false, }) expect(searchParams.attributesToHighlight).toContain('*') @@ -22,7 +23,6 @@ describe('Parameters adapter', () => { ...DEFAULT_CONTEXT, facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], sort: 'id < 1', - finitePagination: false, }) expect(searchParams.filter).toStrictEqual([ @@ -33,6 +33,15 @@ describe('Parameters adapter', () => { expect(searchParams.attributesToHighlight).toContain('*') expect(searchParams.attributesToHighlight?.length).toBe(1) }) + + test('adapting a searchContext with matching strategy', () => { + const searchParams = adaptSearchParams({ + ...DEFAULT_CONTEXT, + matchingStrategy: MatchingStrategies.ALL, + }) + + expect(searchParams.matchingStrategy).toEqual('all') + }) }) describe('Geo rules adapter', () => { @@ -42,7 +51,6 @@ describe('Geo rules adapter', () => { facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], insideBoundingBox: '0,0,0,0', sort: 'id < 1', - finitePagination: false, }) expect(searchParams.filter).toStrictEqual([ @@ -60,7 +68,6 @@ describe('Geo rules adapter', () => { ...DEFAULT_CONTEXT, facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], insideBoundingBox: '0,0,0,0', - finitePagination: false, }) expect(searchParams.filter).toEqual([ @@ -77,7 +84,6 @@ describe('Geo rules adapter', () => { ...DEFAULT_CONTEXT, insideBoundingBox: '0,0,0,0', sort: 'id < 1', - finitePagination: false, }) expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)']) @@ -90,7 +96,6 @@ describe('Geo rules adapter', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, insideBoundingBox: '0,0,0,0', - finitePagination: false, }) expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)']) @@ -132,7 +137,6 @@ describe('Pagination adapter', () => { test('adapting a searchContext with no finite pagination', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, - finitePagination: false, }) expect(searchParams.limit).toBe(7) @@ -142,7 +146,6 @@ describe('Pagination adapter', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, pagination: { paginationTotalHits: 20, page: 1, hitsPerPage: 6 }, - finitePagination: false, }) expect(searchParams.limit).toBe(13) @@ -152,7 +155,6 @@ describe('Pagination adapter', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, pagination: { paginationTotalHits: 20, page: 40, hitsPerPage: 6 }, - finitePagination: false, }) expect(searchParams.limit).toBe(20) @@ -162,7 +164,6 @@ describe('Pagination adapter', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, pagination: { paginationTotalHits: 4, page: 0, hitsPerPage: 6 }, - finitePagination: false, }) expect(searchParams.limit).toBe(4) @@ -173,7 +174,6 @@ describe('Pagination adapter', () => { ...DEFAULT_CONTEXT, query: '', pagination: { paginationTotalHits: 4, page: 0, hitsPerPage: 6 }, - finitePagination: false, placeholderSearch: false, }) @@ -185,7 +185,6 @@ describe('Pagination adapter', () => { ...DEFAULT_CONTEXT, query: '', pagination: { paginationTotalHits: 200, page: 0, hitsPerPage: 6 }, - finitePagination: false, placeholderSearch: true, }) diff --git a/src/adapter/search-request-adapter/search-params-adapter.ts b/src/adapter/search-request-adapter/search-params-adapter.ts index a2368095..46771e57 100644 --- a/src/adapter/search-request-adapter/search-params-adapter.ts +++ b/src/adapter/search-request-adapter/search-params-adapter.ts @@ -32,6 +32,7 @@ export function MeiliParamsCreator(searchContext: SearchContext) { finitePagination, sort, pagination, + matchingStrategy, } = searchContext return { @@ -119,6 +120,11 @@ export function MeiliParamsCreator(searchContext: SearchContext) { } } }, + addMatchingStrategy() { + if (matchingStrategy) { + meiliSearchParams.matchingStrategy = matchingStrategy + } + }, } } @@ -144,6 +150,7 @@ export function adaptSearchParams( meilisearchParams.addFilters() meilisearchParams.addSort() meilisearchParams.addGeoSearchRules() + meilisearchParams.addMatchingStrategy() return meilisearchParams.getParams() } diff --git a/src/contexts/search-context.ts b/src/contexts/search-context.ts index 107b1f63..37e467ef 100644 --- a/src/contexts/search-context.ts +++ b/src/contexts/search-context.ts @@ -3,6 +3,7 @@ import { AlgoliaMultipleQueriesQuery, SearchContext, FacetDistribution, + MatchingStrategies, } from '../types' import { createPaginationContext } from './pagination-context' @@ -37,6 +38,7 @@ export function createSearchContext( placeholderSearch: options.placeholderSearch !== false, // true by default keepZeroFacets: !!options.keepZeroFacets, // false by default finitePagination: !!options.finitePagination, // false by default + matchingStrategy: options.matchingStrategy || MatchingStrategies.LAST, } return searchContext } diff --git a/src/types/types.ts b/src/types/types.ts index 8f1c5faa..972acba2 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -27,6 +27,11 @@ export type ParsedFilter = { value: string } +export const enum MatchingStrategies { + ALL = 'all', + LAST = 'last', +} + export type InstantMeiliSearchOptions = { paginationTotalHits?: number placeholderSearch?: boolean @@ -34,6 +39,7 @@ export type InstantMeiliSearchOptions = { keepZeroFacets?: boolean finitePagination?: boolean clientAgents?: string[] + matchingStrategy?: MatchingStrategies } export type SearchCacheInterface = { @@ -79,6 +85,7 @@ export type SearchContext = Omit & sort?: string placeholderSearch?: boolean primaryKey?: string + matchingStrategy?: MatchingStrategies } export type InstantMeiliSearchInstance = SearchClient & { From 0158b402165b92c9d0e97860672f84a38c7a8db5 Mon Sep 17 00:00:00 2001 From: Charlotte Vermandel Date: Mon, 5 Sep 2022 13:46:38 +0200 Subject: [PATCH 3/3] Simplify context creation --- src/contexts/search-context.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/contexts/search-context.ts b/src/contexts/search-context.ts index 37e467ef..8ed759c8 100644 --- a/src/contexts/search-context.ts +++ b/src/contexts/search-context.ts @@ -38,7 +38,6 @@ export function createSearchContext( placeholderSearch: options.placeholderSearch !== false, // true by default keepZeroFacets: !!options.keepZeroFacets, // false by default finitePagination: !!options.finitePagination, // false by default - matchingStrategy: options.matchingStrategy || MatchingStrategies.LAST, } return searchContext }