• 使用 debouce 处理
  • 使用 usememo 优化搜索逻辑

过滤器

钩子使用 .reduce() 按顺序将每个 filter 函数应用于数据,保持逻辑简洁和模块化:

return filters.reduce(
    (acc, feature) => feature(acc, debouncedQuery),
    dataArray
);

支持不同的搜索

export function search(options) {
    const { fields, matchType } = options;
 
    return (data, query) => {
        const trimmedQuery = String(query).trim().toLowerCase();
 
        if (!trimmedQuery) return data;
 
        return data.filter((item) => {
            const fieldsArray = fields
                ? Array.isArray(fields)
                    ? fields
                    : [fields]
                : getAllKeys(item);
 
            return fieldsArray.some((field) => {
                const fieldValue = getFieldValue(item, field);
                if (fieldValue == null) return false;
 
                const stringValue = convertToString(fieldValue).toLowerCase();
 
                switch (matchType) {
                    case "exact":
                        return stringValue === trimmedQuery;
                    case "startsWith":
                        return stringValue.startsWith(trimmedQuery);
                    case "endsWith":
                        return stringValue.endsWith(trimmedQuery);
                    case "contains":
                        return stringValue.includes(trimmedQuery);
                    default:
                        throw new Error(`Unsupported match type: ${matchType}`);
                }
            });
        });
    };
}

支持模糊搜索

为了实现这一点,我们将使用一种模糊搜索技术来匹配相似的单词,即使它们的拼写不完全相同。我们将使用 n-gram 相似度算法来实现这一点,该算法将单词分成更小的片段 (n-gram) 并进行比较以查找匹配项。

export const nGramFuzzySearch = (value, query) => {
    const n = 2; // Default to bigrams (two-character sequences)
 
    const valueGrams = generateNGrams(value.toLowerCase(), n);
    const queryGrams = generateNGrams(query.toLowerCase(), n);
 
    const intersection = valueGrams.filter((gram) => queryGrams.includes(gram));
 
    return intersection.length / Math.max(valueGrams.length, queryGrams.length);
};
 
const generateNGrams = (str, n) => {
    const grams = [];
 
    for (let i = 0; i <= str.length - n; i++) {
        grams.push(str.slice(i, i + n));
    }
 
    return grams;
};