Skip to content

Commit

Permalink
Better typing of combinator operators and better error on invalid ope…
Browse files Browse the repository at this point in the history
…rator

Restrict the type of the `combineWith` option to only allowed string
values (currently `'or'`, `'and'`, `'and_not'`, the same uppercased or
capitalized, or `undefined`).

Raise a more informative error when an invalid operator is specified.
  • Loading branch information
lucaong committed Jan 24, 2024
1 parent b122658 commit b5495cb
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 8 deletions.
6 changes: 6 additions & 0 deletions src/MiniSearch.test.js
Expand Up @@ -1055,6 +1055,12 @@ describe('MiniSearch', () => {
expect(ms.search('sottomarino vita', { combineWith: 'AND_NOT' }).length).toEqual(0)
})

it('raises an error if combineWith is not a valid operator', () => {
expect(() => {
ms.search('vita cammin', { combineWith: 'XOR' })
}).toThrowError('Invalid combination operator: XOR')
})

it('returns empty results for empty search', () => {
expect(ms.search('')).toEqual([])
expect(ms.search('', { combineWith: 'OR' })).toEqual([])
Expand Down
25 changes: 17 additions & 8 deletions src/MiniSearch.ts
@@ -1,8 +1,11 @@
import SearchableMap from './SearchableMap/SearchableMap'

const OR = 'or'
const AND = 'and'
const AND_NOT = 'and_not'
export type StrictCaseCombinationOperator = 'or' | 'and' | 'and_not'
export type CombinationOperator = StrictCaseCombinationOperator | Uppercase<StrictCaseCombinationOperator> | Capitalize<StrictCaseCombinationOperator>

const OR: StrictCaseCombinationOperator = 'or'
const AND: StrictCaseCombinationOperator = 'and'
const AND_NOT: StrictCaseCombinationOperator = 'and_not'

/**
* Search options to customize the search behavior.
Expand Down Expand Up @@ -95,7 +98,7 @@ export type SearchOptions = {
* search. If "AND" is given, only results matching _all_ the search terms are
* returned by a search.
*/
combineWith?: string,
combineWith?: CombinationOperator,

/**
* Function to tokenize the search query. By default, the same tokenizer used
Expand Down Expand Up @@ -129,7 +132,7 @@ type SearchOptionsWithDefaults = SearchOptions & {

maxFuzzy: number,

combineWith: string
combineWith: CombinationOperator

bm25: BM25Params
}
Expand Down Expand Up @@ -1598,10 +1601,16 @@ export default class MiniSearch<T = any> {
/**
* @ignore
*/
private combineResults (results: RawResult[], combineWith = OR): RawResult {
private combineResults (results: RawResult[], combineWith: CombinationOperator = OR): RawResult {
if (results.length === 0) { return new Map() }
const operator = combineWith.toLowerCase()
return results.reduce(combinators[operator]) || new Map()
const combinator = (combinators as Record<string, CombinatorFunction>)[operator]

if (!combinator) {
throw new Error(`Invalid combination operator: ${combineWith}`)
}

return results.reduce(combinator) || new Map()
}

/**
Expand Down Expand Up @@ -1851,7 +1860,7 @@ const getOwnProperty = (object: any, property: string) =>

type CombinatorFunction = (a: RawResult, b: RawResult) => RawResult

const combinators: { [kind: string]: CombinatorFunction } = {
const combinators: Record<StrictCaseCombinationOperator, CombinatorFunction> = {
[OR]: (a: RawResult, b: RawResult) => {
for (const docId of b.keys()) {
const existing = a.get(docId)
Expand Down

0 comments on commit b5495cb

Please sign in to comment.