UNPKG

@meilisearch/instant-meilisearch

Version:
452 lines (381 loc) 13.7 kB
import { describe, test, expect } from 'vitest' import { MatchingStrategies } from 'meilisearch' import { adaptSearchParams } from '../search-params-adapter.js' import type { OverridableMeiliSearchSearchParameters, SearchContext, } from '../../../types/index.js' const DEFAULT_CONTEXT: SearchContext = { indexUid: 'test', pagination: { page: 0, hitsPerPage: 6, finite: false }, placeholderSearch: true, keepZeroFacets: false, attributesToHighlight: ['*'], highlightPreTag: '<mark>', highlightPostTag: '</mark>', } describe('Parameters adapter', () => { test('adapting a basic searchContext ', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, }) expect(searchParams.attributesToHighlight).toContain('*') expect(searchParams.attributesToHighlight?.length).toBe(1) }) test('adapting a searchContext with filters and sort', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], sort: 'id < 1', }) expect(searchParams.filter).toStrictEqual([ ['"genres"="Drama"', '"genres"="Thriller"'], ['"title"="Ariel"'], ]) expect(searchParams.sort).toStrictEqual(['id < 1']) expect(searchParams.attributesToHighlight).toContain('*') expect(searchParams.attributesToHighlight?.length).toBe(1) }) test('adapting multi-word filters', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, facetFilters: [['My Genre:Science Fiction']], }) expect(searchParams.filter).toStrictEqual([ ['"My Genre"="Science Fiction"'], ]) }) test('adapting a searchContext with overridden Meilisearch parameters', () => { const meiliSearchParams: OverridableMeiliSearchSearchParameters = { attributesToHighlight: ['movies', 'genres'], highlightPreTag: '<em>', highlightPostTag: '</em>', matchingStrategy: MatchingStrategies.ALL, } const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, meiliSearchParams, }) expect(searchParams.attributesToHighlight).toEqual( meiliSearchParams.attributesToHighlight ) expect(searchParams.highlightPreTag).toEqual( meiliSearchParams.highlightPreTag ) expect(searchParams.highlightPostTag).toEqual( meiliSearchParams.highlightPostTag ) expect(searchParams.matchingStrategy).toEqual( meiliSearchParams.matchingStrategy ) }) test('adapting a searchContext with overridden Meilisearch parameters for a specific index', () => { const meiliSearchParams: OverridableMeiliSearchSearchParameters = { attributesToHighlight: ['movies', 'genres'], highlightPreTag: '<em>', highlightPostTag: '</em>', matchingStrategy: MatchingStrategies.ALL, indexesOverrides: { test: { attributesToHighlight: ['release_date'], highlightPreTag: '<span>', highlightPostTag: '</span>', 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, embedder: 'default', } const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, meiliSearchParams: { hybrid: hybridSearchConfig, }, }) 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] const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, meiliSearchParams: { vector, }, }) 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 const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, meiliSearchParams: { rankingScoreThreshold, }, }) 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' const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, meiliSearchParams: { distinct: distinctSearchConfig, }, }) 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) }) test('filter can be set via global override when IS filters are empty', () => { const globalFilter = 'category = "books"' const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, // No facetFilters, filters, or numericFilters - empty IS filters meiliSearchParams: { filter: globalFilter, }, }) expect(searchParams.filter).toBe(globalFilter) }) test('filter can be set via per-index override when IS filters are empty', () => { const globalFilter = 'category = "books"' const indexFilter = 'status = "active"' const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, // No facetFilters, filters, or numericFilters - empty IS filters meiliSearchParams: { filter: globalFilter, indexesOverrides: { test: { filter: indexFilter }, }, }, }) // Per-index override should take precedence over global override expect(searchParams.filter).toBe(indexFilter) }) }) describe('Geo filter adapter', () => { test('adapting a searchContext with filters, sort and geo filters ', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], insideBoundingBox: '0,0,0,0', sort: 'id < 1', }) expect(searchParams.filter).toStrictEqual([ '_geoBoundingBox([0, 0], [0, 0])', ['"genres"="Drama"', '"genres"="Thriller"'], ['"title"="Ariel"'], ]) expect(searchParams.sort).toStrictEqual(['id < 1']) expect(searchParams.attributesToHighlight).toContain('*') expect(searchParams.attributesToHighlight?.length).toBe(1) }) test('adapting a searchContext with only facetFilters and geo filters ', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], insideBoundingBox: '0,0,0,0', }) expect(searchParams.filter).toEqual([ '_geoBoundingBox([0, 0], [0, 0])', ['"genres"="Drama"', '"genres"="Thriller"'], ['"title"="Ariel"'], ]) expect(searchParams.attributesToHighlight).toContain('*') expect(searchParams.attributesToHighlight?.length).toBe(1) }) test('adapting a searchContext with only sort and geo filters ', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, insideBoundingBox: '0,0,0,0', sort: 'id < 1', }) expect(searchParams.filter).toEqual(['_geoBoundingBox([0, 0], [0, 0])']) expect(searchParams.sort).toStrictEqual(['id < 1']) expect(searchParams.attributesToHighlight).toContain('*') expect(searchParams.attributesToHighlight?.length).toBe(1) }) test('adapting a searchContext with no sort and no filters and geo filters ', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, insideBoundingBox: '0,0,0,0', }) expect(searchParams.filter).toEqual(['_geoBoundingBox([0, 0], [0, 0])']) expect(searchParams.attributesToHighlight).toContain('*') expect(searchParams.attributesToHighlight?.length).toBe(1) }) }) describe('Pagination adapter', () => { test('adapting a searchContext with finite pagination', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, pagination: { page: 0, hitsPerPage: 6, finite: true }, }) expect(searchParams.page).toBe(1) expect(searchParams.hitsPerPage).toBe(6) }) test('adapting a searchContext with finite pagination on a later page', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, pagination: { page: 10, hitsPerPage: 6, finite: true }, }) expect(searchParams.page).toBe(11) expect(searchParams.hitsPerPage).toBe(6) }) test('adapting a searchContext with no finite pagination on page 1', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, }) expect(searchParams.limit).toBe(7) expect(searchParams.offset).toBe(0) }) test('adapting a searchContext with no finite pagination on page 2', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, pagination: { page: 1, hitsPerPage: 6, finite: false }, }) expect(searchParams.limit).toBe(7) expect(searchParams.offset).toBe(6) }) test('adapting a finite pagination with no placeholderSearch', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, query: '', pagination: { page: 4, hitsPerPage: 6, finite: true }, placeholderSearch: false, }) expect(searchParams.page).toBe(5) expect(searchParams.hitsPerPage).toBe(0) }) test('adapting a finite pagination with no placeholderSearch and a query', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, query: 'a', pagination: { page: 4, hitsPerPage: 6, finite: true }, placeholderSearch: false, }) expect(searchParams.page).toBe(5) expect(searchParams.hitsPerPage).toBeGreaterThan(0) }) test('adapting a finite pagination with no placeholderSearch and a facetFilter', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, query: '', pagination: { page: 4, hitsPerPage: 6, finite: true }, placeholderSearch: false, facetFilters: ['genres:Action'], }) expect(searchParams.page).toBe(5) expect(searchParams.hitsPerPage).toBeGreaterThan(0) }) test('adapting a scroll pagination with no placeholderSearch', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, query: '', pagination: { page: 4, hitsPerPage: 6, finite: false }, placeholderSearch: false, }) expect(searchParams.limit).toBe(0) expect(searchParams.offset).toBe(0) }) test('adapting a scroll pagination with no placeholderSearch and a query', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, query: 'a', pagination: { page: 4, hitsPerPage: 6, finite: false }, placeholderSearch: false, }) expect(searchParams.limit).toBeGreaterThan(0) expect(searchParams.offset).toBeGreaterThan(0) }) test('adapting a scroll pagination with no placeholderSearch and a facetFilter', () => { const searchParams = adaptSearchParams({ ...DEFAULT_CONTEXT, query: 'a', pagination: { page: 4, hitsPerPage: 6, finite: false }, placeholderSearch: false, facetFilters: ['genres:Action'], }) expect(searchParams.limit).toBeGreaterThan(0) expect(searchParams.offset).toBeGreaterThan(0) }) })