UNPKG

quick-score

Version:

A JavaScript string-scoring and fuzzy-matching library based on the Quicksilver algorithm, designed for smart auto-complete.

464 lines (429 loc) 17.8 kB
/** * @typedef {function(string, string, Array<RangeTuple>?): number} ScorerFunction * A function that takes `string` and `query` parameters and returns a floating * point number between 0 and 1 that represents how well the `query` matches the * `string`. It defaults to the [quickScore()]{@link quickScore} function in * this library. * * If the function gets a third `matches` parameter, it should fill the passed-in * array with indexes corresponding to where the query matches the string, as * described in the [search()]{@link QuickScore#search} method. * * @param {string} string The string to score. * @param {string} query The query string to score the `string` parameter against. * @param {Array<RangeTuple>} [matches] If supplied, the function should push * onto `matches` a tuple with start and end indexes for each substring range * of `string` that matches `query`. * @returns {number} A number between 0 and 1 that represents how well the * `query` matches the `string`. */ type ScorerFunction = (string: string, query: string, matches?: [number, number][]) => number; /** * @typedef {function(string): string} TransformStringFunction A function that * takes a `string` parameter and returns a transformed version of that string. * * @param {string} string The string to be transformed. * @returns {string} The string with the transform applied to it. */ type TransformStringFunction = (string: string) => string; /** * @typedef {string|string[]} KeyName A reference to an item's key to search. * The key names can point to a nested key by passing either a dot-delimited * string or an array of sub-keys that specify the path to the value. */ type KeyName = string | string[]; /** * @typedef {KeyName|{name: KeyName, scorer: ScorerFunction}} ItemKey A * reference to an item's key to search. This type can also include a custom * scoring function to use for the given key. * * @property {KeyName} [name] The name of a key to search. * @property {ScorerFunction} [scorer] The function that will be used to score * the named string. */ type ItemKey = (KeyName | { name: KeyName, scorer: ScorerFunction }); /** * @typedef {object} Options An object specifying various options that can * customize QuickScore's scoring behavior. * * @property {Array<ItemKey>} [keys] An array that specifies which keys to * search. * @property {string} [sortKey] The name of the key that will be used to sort * items with identical scores. * @property {number} [minimumScore] The minimum score an item must have to * appear in the results returned from `search()`. * @property {TransformStringFunction} [transformString] A function that takes * a `string` parameter and returns a transformed version of that string. * @property {ScorerFunction} [scorer] A function that takes `string` and * `query` parameters and returns a floating point number between 0 and 1 that * represents how well the `query` matches the `string`. * @property {Config} [config] An object that is passed to the scorer function * to further customize its behavior. */ interface Options { keys?: ItemKey[], sortKey?: string, minimumScore?: number, transformString?: TransformStringFunction, scorer?: ScorerFunction, config?: Config } /** * @typedef {object} ScoredString An object representing the results of scoring * an `items` array that contains strings. * * @property {string} item The string that was scored. * @property {number} score The floating point score of the string for the * current query. * @property {Array<RangeTuple>} matches An array of tuples that specify the * character ranges where the query matched the string. */ interface ScoredString<T> { item: T, score: number, matches: RangeTuple[], } /** * @typedef {object} ScoredObject An object representing the results of scoring * an `items` array that contains objects. * * @property {object} item The object that was scored. * @property {number} score The highest score from among the individual key scores. * @property {string} scoreKey The name of the key with the highest score, * which will be an empty string if they're all zero. * @property {string} scoreValue The value of the key with the highest score, * which makes it easier to access if it's a nested string. * @property {object} scores A hash of the individual scores for each key. * @property {object} matches A hash of arrays that specify the character * ranges of the query match for each key. * @property {object} _ An internal cache of the transformed versions of this * item's strings and other metadata, which can be ignored. */ interface ScoredObject<T> { item: T, score: number, scoreKey: string, scoreValue: string, scores: { [k: string]: number }, matches: { [k: string]: RangeTuple[] }, _?: unknown } type ScoredResult<T> = T extends string ? ScoredString<T> : ScoredObject<T>; /** * A class for scoring and sorting a list of items against a query string. Each * item receives a floating point score between `0` and `1`. */ declare class QuickScore<T> { /** * @memberOf QuickScore.prototype * @member {Array<T>} items The array of items to search, which * should only be modified via the [setItems()]{@link QuickScore#setItems} * method. * @readonly */ items: readonly T[]; /** * @memberOf QuickScore.prototype * @member {Array<ItemKey>} keys The keys to search on each item, which * should only be modified via the [setItems()]{@link QuickScore#setKeys} * method. * @readonly */ keys: readonly ItemKey[]; /** * @param {Array<T>} [items] The list of items to score. If * the list is not a flat array of strings, a `keys` array must be supplied * via the second parameter. The `items` array is not modified by QuickScore. * * @param {Array<ItemKey>|Options} [options] If the `items` parameter * is an array of flat strings, the `options` parameter can be left out. If * it is a list of objects containing keys that should be scored, the * `options` parameter must either be an array of key names or an object * containing a `keys` property. * * @param {Array<ItemKey>} [options.keys] In the simplest case, an array of * key names to score on the objects in the `items` array. * * The key names can point to a nested key by passing either a dot-delimited * string or an array of sub-keys that specify the path to the value. So a * key `name` of `"foo.bar"` would evaluate to `"baz"` given an object like * `{ foo: { bar: "baz" } }`. Alternatively, that path could be passed as * an array, like `["foo", "bar"]`. In either case, if this sub-key's match * produces the highest score for an item in the search results, its * `scoreKey` name will be `"foo.bar"`. * * If your items have keys that contain periods, e.g., `"first.name"`, but * you don't want these names to be treated as paths to nested keys, simply * wrap the name in an array, like `{ keys: ["ssn", ["first.name"], * ["last.name"]] }`. * * Instead of a string or string array, an item in `keys` can also be passed * as a `{name, scorer}` object, which lets you specify a different scoring * function for each key. The scoring function should behave as described * next. * * @param {string} [options.sortKey=options.keys[0]] An optional key name * that will be used to sort items with identical scores. Defaults to the * name of the first item in the `keys` parameter. If `sortKey` points to * a nested key, use a dot-delimited string instead of an array to specify * the path. * * @param {number} [options.minimumScore=0] An optional value that * specifies the minimum score an item must have to appear in the results * returned from [search()]{@link QuickScore#search}. Defaults to `0`, * so items that don't match the full `query` will not be returned. This * value is ignored if the `query` is empty or undefined, in which case all * items are returned, sorted alphabetically and case-insensitively on the * `sortKey`, if any. * * @param {TransformStringFunction} [options.transformString] An optional * function that takes a `string` parameter and returns a transformed * version of that string. This function will be called on each of the * searchable keys in the `items` array as well as on the `query` * parameter to the `search()` method. The default function calls * `toLocaleLowerCase()` on each string, for a case-insensitive search. The * result of this function is cached for each searchable key on each item. * * You can pass a function here to do other kinds of preprocessing, such as * removing diacritics from all the strings or converting Chinese characters * to pinyin. For example, you could use the * [`latinize`](https://www.npmjs.com/package/latinize) npm package to * convert characters with diacritics to the base character so that your * users can type an unaccented character in the query while still matching * items that have accents or diacritics. Pass in an `options` object like * this to use a custom `transformString()` function: * `{ transformString: s => latinize(s.toLocaleLowerCase()) }` * * @param {ScorerFunction} [options.scorer] An optional function that takes * `string` and `query` parameters and returns a floating point number * between 0 and 1 that represents how well the `query` matches the * `string`. It defaults to the [quickScore()]{@link quickScore} function * in this library. * * If the function gets a third `matches` parameter, it should fill the * passed-in array with indexes corresponding to where the query * matches the string, as described in the [search()]{@link QuickScore#search} * method. * * @param {Config} [options.config] An optional object that is passed to * the scorer function to further customize its behavior. If the * `scorer` function has a `createConfig()` method on it, the `QuickScore` * instance will call that with the `config` value and store the result. * This can be used to extend the `config` parameter with default values. */ constructor( items?: readonly T[], options?: readonly ItemKey[] | Options ); /** * Scores the instance's items against the `query` and sorts them from * highest to lowest. * * @param {string} query The string to score each item against. The * instance's `transformString()` function is called on this string before * it's matched against each item. * * @returns {Array<ScoredResult<T>>} When the instance's `items` * are flat strings, an array of `ScoredString` objects containing the * following properties is returned: * * - `item`: the string that was scored * - `score`: the floating point score of the string for the current query * - `matches`: an array of arrays that specify the character ranges * where the query matched the string * * When the `items` are objects, an array of `ScoredObject` results is * returned: * * - `item`: the object that was scored * - `score`: the highest score from among the individual key scores * - `scoreKey`: the name of the key with the highest score, which will be * an empty string if they're all zero * - `scoreValue`: the value of the key with the highest score, which makes * it easier to access if it's a nested string * - `scores`: a hash of the individual scores for each key * - `matches`: a hash of arrays that specify the character ranges of the * query match for each key * * The results array is sorted high to low on each item's score. Items with * identical scores are sorted alphabetically and case-insensitively on the * `sortKey` option. Items with scores that are <= the `minimumScore` option * (defaults to `0`) are not returned, unless the `query` is falsy, in which * case all of the items are returned, sorted alphabetically. * * The start and end indices in each `RangeTuple` in the `matches` array * can be used as parameters to the `substring()` method to extract the * characters from each string that match the query. This can then be used * to format the matching characters with a different color or style. * * Each `ScoredObject` item also has a `_` property, which caches transformed * versions of the item's strings, and might contain additional internal * metadata in the future. It can be ignored. */ search(query: string): ScoredResult<T>[]; /** * Sets the `keys` configuration. `setItems()` must be called after * changing the keys so that the items' transformed strings get cached. * * @param {Array<ItemKey>} keys List of keys to score, as either strings * or `{name, scorer}` objects. * * @param {string} [sortKey=keys[0]] Name of key on which to sort * identically scored items. Defaults to the first `keys` item. */ setKeys(keys: readonly ItemKey[], sortKey?: string): void; /** * Sets the `items` array and caches a transformed copy of all the item * strings specified by the `keys` parameter to the constructor, using the * `transformString` option (which defaults to `toLocaleLowerCase()`). * * @param {T[]} items List of items to score. */ setItems(items: readonly T[]): void; } /** * A class representing a half-open interval of characters. A range's `location` * property and `max()` value can be used as arguments for the `substring()` * method to extract a range of characters. */ declare class Range { /** * @param {number} [location=-1] - Starting index of the range. * @param {number} [length=0] - Number of characters in the range. */ constructor(location?: number, length?: number); location: number; length: number; /** * Gets the end index of the range, which indicates the character * immediately after the last one in the range. * * @returns {number} */ /** * Sets the end index of the range, which indicates the character * immediately after the last one in the range. * * @param {number} [value] - End of the range. * * @returns {number} */ max(value?: number): number; /** * Returns whether the range contains a location >= 0. * * @returns {boolean} */ isValid(): boolean; /** * Returns a tuple of the range's start and end indexes. * * @returns {RangeTuple} */ toArray(): RangeTuple; /** * Returns a string representation of the range's open interval. * * @returns {string} */ toString(): string; } /** * @typedef {Array<number>} RangeTuple A tuple containing a range's start and * end indexes. * * @property {number} 0 Start index. * @property {number} 1 End index. */ type RangeTuple = [number, number]; interface ConfigOptions { wordSeparators?: string, uppercaseLetters?: string, ignoredScore?: number, skippedScore?: number, emptyQueryScore?: number, maxIterations?: number, } interface QSConfigOptions extends ConfigOptions { longStringLength: number, maxMatchStartPct: number, minMatchDensityPct: number, maxMatchDensityPct: number, beginningOfStringPct: number } declare class Config { constructor(options: ConfigOptions); useSkipReduction( string: string, query: string, remainingScore: number, searchRange: Range, remainingSearchRange: Range, matchedRange: Range, fullMatchedRange: Range ): boolean; adjustRemainingScore( string: string, query: string, remainingScore: number, skippedSpecialChar: boolean, searchRange: Range, remainingSearchRange: Range, matchedRange: Range, fullMatchedRange: Range ): number; } declare class QuickScoreConfig extends Config { } declare function createConfig( options: Config | ConfigOptions | QSConfigOptions ): Config; declare const DefaultConfig: QuickScoreConfig; declare const BaseConfig: Config; declare const QuicksilverConfig: Config; /** * Scores a string against a query. * * @param {string} string The string to score. * * @param {string} query The query string to score the `string` parameter against. * * @param {Array<RangeTuple>} [matches] If supplied, `quickScore()` will push * onto `matches` a tuple with start and end indexes for each substring range of * `string` that matches `query`. These indexes can be used to highlight the * matching characters in an auto-complete UI. * * @param {string} [transformedString] A transformed version of the string that * will be used for matching. This defaults to a lowercase version of `string`, * but it could also be used to match against a string with all the diacritics * removed, so an unaccented character in the query would match an accented one * in the string. * * @param {string} [transformedQuery] A transformed version of `query`. The * same transformation applied to `transformedString` should be applied to this * parameter, or both can be left as `undefined` for the default lowercase * transformation. * * @param {object} [config] A configuration object that can modify how the * `quickScore` algorithm behaves. * * @param {Range} [stringRange] The range of characters in `string` that should * be checked for matches against `query`. Defaults to the entire `string` * parameter. * * @returns {number} A number between 0 and 1 that represents how well the * `query` matches the `string`. */ declare function quickScore( string: string, query: string, matches?: RangeTuple[], transformedString?: string, transformedQuery?: string, config?: Config, stringRange?: Range ): number; declare namespace quickScore { export { createConfig }; } export { BaseConfig, Config, ConfigOptions, DefaultConfig, ItemKey, KeyName, Options, QSConfigOptions, QuickScore, QuickScoreConfig, QuicksilverConfig, Range, RangeTuple, ScoredObject, ScoredResult, ScoredString, ScorerFunction, TransformStringFunction, createConfig, quickScore };