ss-search
Version:
The most basic, yet powerful text search.
65 lines (63 loc) • 2.86 kB
JavaScript
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
};