Написание собственных фильтров

В Awesomplete механизм фильтрации является ключевым элементом, определяющим поведение списка подсказок при вводе текста. По умолчанию библиотека использует простой алгоритм сопоставления подстроки, однако архитектура позволяет полностью заменить его собственной реализацией через параметр filter.

Фильтр в Awesomplete — это функция, которая определяет, должна ли конкретная запись из массива данных отображаться в выпадающем списке. Именно этот слой отвечает за логику поиска, будь то строгое совпадение, префиксный поиск, нечёткий поиск или сложные пользовательские правила.


Сигнатура и поведение фильтрующей функции

Фильтр передаётся как функция с фиксированной сигнатурой:

function filter(text, input) {
    return true | false;
}

Где:

  • text — элемент массива источника данных (строка или объект после нормализации через item)
  • input — текущее значение поля ввода

Возвращаемое значение:

  • true — элемент включается в список
  • false — элемент исключается

Контекст выполнения функции (this) обычно связан с экземпляром Awesomplete, что позволяет получать доступ к дополнительным параметрам компонента.


Стандартный фильтр и его замена

Встроенная логика Awesomplete основана на проверке вхождения подстроки:

Awesomplete.FILTER_CONTAINS = function (text, input) {
    return text.indexOf(input) !== -1;
};

Однако этот подход можно заменить на любой другой алгоритм:

var aw = new Awesomplete(inputElement, {
    filter: function(text, input) {
        return text.startsWith(input);
    }
});

Здесь используется префиксный поиск, при котором подсказка появляется только если строка начинается с введённого текста.


Реализация фильтра без учёта регистра

Одним из наиболее распространённых расширений является игнорирование регистра:

function caseInsensitiveFilter(text, input) {
    return text.toLowerCase().indexOf(input.toLowerCase()) !== -1;
}

Такая реализация увеличивает удобство поиска, особенно при работе с пользовательскими именами, тегами или смешанным текстом.


Префиксный фильтр и строгие совпадения

Фильтрация по началу строки часто используется в автодополнении команд:

function startsWithFilter(text, input) {
    return text.slice(0, input.length) === input;
}

Более строгая версия, учитывающая полное совпадение:

function exactMatchFilter(text, input) {
    return text === input;
}

Такой фильтр фактически превращает Awesomplete в инструмент выбора из фиксированного набора значений.


Нечёткий поиск (fuzzy matching)

Одним из наиболее полезных расширений является реализация нечёткого поиска, позволяющего находить совпадения по символам в неправильном порядке или с пропусками.

Базовая реализация:

function fuzzyFilter(text, input) {
    let tIndex = 0;
    let iIndex = 0;

    text = text.toLowerCase();
    input = input.toLowerCase();

    while (tIndex < text.length && iIndex < input.length) {
        if (text[tIndex] === input[iIndex]) {
            iIndex++;
        }
        tIndex++;
    }

    return iIndex === input.length;
}

Алгоритм проверяет, можно ли «собрать» input из последовательности символов text.


Работа с объектами данных

Awesomplete позволяет использовать не только строки, но и объекты. В этом случае фильтр должен учитывать структуру данных.

Пример:

var aw = new Awesomplete(inputElement, {
    item: function(text) {
        return text.label;
    },
    filter: function(text, input) {
        return text.label.toLowerCase().includes(input.toLowerCase());
    }
});

Здесь фильтрация выполняется по полю label, что позволяет строить сложные списки (например, с метаданными).


Комбинирование условий фильтрации

Фильтр может объединять несколько логик одновременно:

function combinedFilter(text, input) {
    input = input.toLowerCase();
    text = text.toLowerCase();

    return text.startsWith(input) || text.includes(input);
}

Такой подход позволяет отдавать приоритет точным совпадениям, но сохранять гибкость поиска.


Использование регулярных выражений

Регулярные выражения позволяют создавать мощные фильтры:

function regexFilter(text, input) {
    try {
        var re = new RegExp(input, "i");
        return re.test(text);
    } catch (e) {
        return false;
    }
}

Здесь:

  • i — игнорирование регистра
  • обработка ошибок защищает от некорректных выражений

Такой фильтр подходит для продвинутых интерфейсов поиска.


Оптимизация фильтров

При работе с большими массивами данных фильтр становится критическим для производительности. Основные принципы оптимизации:

Предобработка входных данных

var normalizedInput = input.toLowerCase();

Кэширование преобразований

var cache = new Map();

function cachedFilter(text, input) {
    if (!cache.has(text)) {
        cache.set(text, text.toLowerCase());
    }

    return cache.get(text).includes(input.toLowerCase());
}

Минимизация операций внутри фильтра

Любые тяжёлые вычисления должны выноситься из функции фильтрации.


Фильтрация с учётом языка и Unicode

При работе с кириллицей и другими Unicode-символами важно учитывать нормализацию строк:

function unicodeFilter(text, input) {
    text = text.normalize("NFKD").toLowerCase();
    input = input.normalize("NFKD").toLowerCase();

    return text.includes(input);
}

Это снижает влияние диакритических знаков и различий в кодировке символов.


Приоритеты и ограничение результатов

Фильтр отвечает только за включение или исключение элементов, однако его часто комбинируют с сортировкой для создания приоритетов:

function prioritizedFilter(text, input) {
    input = input.toLowerCase();
    text = text.toLowerCase();

    if (text === input) return true;
    if (text.startsWith(input)) return true;
    if (text.includes(input)) return true;

    return false;
}

Такая структура фильтра создаёт многоуровневую систему релевантности.


Влияние filter на поведение Awesomplete

Фильтр напрямую влияет на:

  • количество отображаемых подсказок
  • скорость обновления списка
  • субъективное качество поиска
  • взаимодействие с sort и replace

Любые изменения в логике фильтра немедленно отражаются на UX автодополнения, поскольку именно он определяет набор данных, передаваемых дальше по конвейеру обработки Awesomplete.