UNPKG

ss-search

Version:

The most basic, yet powerful text search.

65 lines (63 loc) 2.86 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/lib/ss-search.ts import { get, deburr, escapeRegExp, memoize, round } from "lodash-es"; var normalize = /* @__PURE__ */ __name((text) => deburr(text).replace(/[\u0300-\u036f]/g, "").toLocaleLowerCase().trim(), "normalize"); var tokenize = /* @__PURE__ */ __name((searchText) => normalize(escapeRegExp(searchText)).match(/[\p{L}\d]+/gimu) || [], "tokenize"); var convertToSearchableStrings = memoize((elements, searchableKeys, cacheKey) => { if (!elements || elements.length === 0 || !searchableKeys || searchableKeys.length === 0) { return []; } const arraySelectorRegex = /\[(.*)]/; return elements.map((element) => searchableKeys.map((key) => { const value = get(element, key.replace(arraySelectorRegex, "")); if (value === null || value === void 0 || typeof value === "function") { return ""; } const arraySelector = get(arraySelectorRegex.exec(key), "1"); if (arraySelector) { return value.map((x) => get(x, arraySelector)); } if (Array.isArray(value) || typeof value === "object") { return JSON.stringify(value); } return value; }).reduce((a, b) => a + b, "")).map((x) => normalize(x)); }, (elements, _, cacheKey) => cacheKey ?? elements); var indexDocuments = convertToSearchableStrings; var getScore = /* @__PURE__ */ __name((matchesAllSearchWords, searchWords, searchableDataString) => { if (!matchesAllSearchWords) { return 0; } const searchableDataStringWithoutNonWordCharacters = searchableDataString.replace(/[^\p{L}\d]+/gimu, ""); const remainingTextAfterRemovingSearchWords = searchWords.sort((a, b) => b.length - a.length).reduce((remainingText, searchWord) => remainingText.replace(new RegExp(searchWord, "gm"), ""), searchableDataStringWithoutNonWordCharacters); return round(1 - remainingTextAfterRemovingSearchWords.length / searchableDataStringWithoutNonWordCharacters.length, 4); }, "getScore"); function search(elements, searchableKeys, searchText, options) { const searchWords = tokenize(searchText); const searchableDataStrings = convertToSearchableStrings(elements, searchableKeys, options?.cacheKey); return searchableDataStrings.reduce((accumulator, x, i) => { const matchesAllSearchWords = searchWords.every((searchWord) => x.includes(searchWord)); if (options?.withScore) { const score = getScore(matchesAllSearchWords, searchWords, x); accumulator.push({ element: elements[i], score }); return accumulator; } if (matchesAllSearchWords) { accumulator.push(elements[i]); } return accumulator; }, []); } __name(search, "search"); export { convertToSearchableStrings, getScore, indexDocuments, normalize, search, tokenize };