Методы поиска и фильтрации

Базовая модель поиска: индексируемые поля и стратегия сопоставления

В основе поиска Tom Select лежит принцип поиска по заранее определённым полям объекта данных. Каждый элемент списка рассматривается как объект, а не как строка. Это позволяет строить гибкие стратегии фильтрации без необходимости преобразования данных в текстовый формат.

Ключевая настройка, определяющая поведение поиска:

searchField: ['title', 'description']

Каждое поле проходит нормализацию и участвует в процессе сопоставления. По умолчанию используется нечёткий поиск по подстроке с учётом регистра и токенизации.

Внутренний процесс можно описать как последовательность:

  1. Извлечение значений из указанных полей
  2. Нормализация текста (lowercase, очистка символов)
  3. Разбиение запроса на токены
  4. Сопоставление токенов с данными
  5. Вычисление веса совпадения

Нормализация данных и запросов

Перед сравнением строк Tom Select приводит данные к единому виду. Это снижает влияние регистра, пунктуации и нестандартных символов.

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

normalize: function(str) {
    return str.toLowerCase().replace(/[^a-z0-9\s]/gi, '');
}

Фактическая реализация может отличаться в зависимости от версии, но логика остаётся аналогичной.

Важные аспекты:

  • приведение к нижнему регистру
  • удаление спецсимволов
  • унификация пробелов
  • разбиение на токены

Алгоритм токенизации и сопоставления

Поисковая строка разбивается на набор токенов. Каждый токен сравнивается с каждым полем объекта.

Пример:

query: "red apple"
item: { title: "Fresh Red Apple", category: "fruit" }

Токены:

["red", "apple"]

Система проверяет:

  • наличие “red” в title
  • наличие “apple” в title
  • частичное совпадение токенов

Совпадение оценивается по весовой модели.


Режимы логики объединения условий

Tom Select поддерживает несколько стратегий объединения условий поиска.

AND-логика (строгое совпадение)

searchConjunction: 'and'

Элементы возвращаются только при совпадении всех токенов.

Пример:

  • запрос: "red apple"
  • результат: только элементы, содержащие и red, и apple

OR-логика (расширенный поиск)

searchConjunction: 'or'

Возвращаются элементы, содержащие хотя бы один токен.

Особенность:

  • повышает полноту результатов
  • снижает точность при длинных запросах

Управление релевантностью: scoring-модель

Каждое совпадение получает числовую оценку. Итоговая сортировка строится на основе этого значения.

Факторы влияния:

  • совпадение начала строки (prefix match)
  • полное совпадение слова
  • частичное вхождение
  • количество совпавших полей
  • длина строки (короткие совпадения получают более высокий вес)

Условная модель оценки:

score = (prefix_bonus * 3)
      + (exact_match * 2)
      + (substring_match * 1)
      - (length_penalty)

Хотя формула не фиксирована в API, поведение отражает подобный принцип.


Настройка поисковых полей

Поиск напрямую зависит от структуры данных.

new TomSelect('#select', {
    options: [
        { id: 1, title: 'Apple', category: 'Fruit' },
        { id: 2, title: 'Banana', category: 'Fruit' }
    ],
    searchField: ['title', 'category']
});

Особенности:

  • поддерживаются вложенные поля через точечную нотацию
  • допускается массив полей
  • порядок полей влияет на приоритет

Пример вложенного доступа:

searchField: ['data.title', 'data.tags']

Кастомизация фильтрации через score

Расширенная модель поиска позволяет переопределять механизм ранжирования:

score: function(search) {
    return function(item) {
        let score = 0;

        if (item.title.includes(search)) {
            score += 10;
        }

        if (item.category === search) {
            score += 5;
        }

        return score;
    };
}

Возвращаемая функция должна выдавать числовое значение релевантности.

Особенности:

  • ноль означает отсутствие совпадения
  • отрицательные значения используются для понижения приоритета
  • сортировка происходит по убыванию score

Переопределение поиска через filter

Фильтрация может быть полностью заменена пользовательской логикой:

filter: function(item, query) {
    return item.title.startsWith(query);
}

В отличие от score, filter возвращает булево значение.

Применение:

  • строгие правила включения
  • бинарная фильтрация без ранжирования
  • быстрые проверки без сложной логики

Поиск с удалённой загрузкой данных

Tom Select поддерживает динамическую фильтрацию через сервер:

load: function(query, callback) {
    fetch('/api/search?q=' + encodeURIComponent(query))
        .then(res => res.json())
        .then(data => callback(data));
}

Особенности архитектуры:

  • локальный поиск отключается
  • каждый ввод инициирует запрос
  • сервер становится источником фильтрации

Ключевые аспекты:

  • минимизация клиентской нагрузки
  • возможность сложных SQL/NoSQL запросов
  • централизованная логика релевантности

Ограничение количества результатов

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

maxOptions: 10

Поведение:

  • после сортировки обрезается список
  • влияет только на отображение, не на поиск
  • полезно при больших наборах данных

Кеширование поисковых результатов

При частых запросах используется внутреннее кеширование:

  • ключ: строка запроса
  • значение: список результатов
  • сброс при изменении options

Кеш снижает количество вычислений при повторяющихся вводах.


Реакция на ввод: событие onSearch

Поиск сопровождается событием:

onSearch: function(query) {
    console.log('search:', query);
}

Используется для:

  • аналитики пользовательского ввода
  • динамического изменения UI
  • синхронизации с внешними компонентами

Асинхронная модификация результатов

Фильтрация может быть расширена через onLoad и onDropdownOpen:

onDropdownOpen: function() {
    this.refreshOptions();
}

Механизм позволяет:

  • пересчитывать фильтр при каждом открытии
  • обновлять данные без перезагрузки страницы
  • комбинировать локальные и серверные источники

Чувствительность к регистру и Unicode

Tom Select нормализует строки, но поведение может отличаться при работе с Unicode:

  • латиница обрабатывается стандартно
  • кириллица требует корректной нормализации
  • диакритические знаки могут игнорироваться или сохраняться

Пример влияния:

"café" ≈ "cafe"

Производительность поиска

Эффективность фильтрации зависит от:

  • количества элементов
  • числа searchField
  • сложности score-функции
  • частоты ввода пользователя

Оптимизации:

  • минимизация числа полей поиска
  • использование предвычисленных индексов
  • отключение лишних callback-ов
  • применение server-side filtering при больших данных

Комбинирование локального и удалённого поиска

Гибридная модель:

new TomSelect('#select', {
    load: function(query, callback) {
        if (query.length < 2) return callback([]);

        fetch('/api?q=' + query)
            .then(r => r.json())
            .then(callback);
    },
    searchField: ['title']
});

Поведение:

  • короткие запросы обрабатываются локально
  • длинные — через API
  • результаты могут объединяться

Переопределение поиска через plugins

Плагины могут модифицировать фильтрацию:

  • изменение tokenization
  • расширение scoring
  • добавление fuzzy matching
  • интеграция с внешними библиотеками (например, Fuse.js)

Пример интеграции:

score: function(search) {
    const fuse = new Fuse(this.options, {
        keys: ['title']
    });

    return function(item) {
        return fuse.search(search).length;
    };
}

Итоговая модель обработки запроса

Полный цикл поиска в Tom Select:

  1. Ввод строки пользователем
  2. Нормализация запроса
  3. Токенизация
  4. Поиск по searchField
  5. Вычисление score
  6. Сортировка
  7. Применение filter (если задан)
  8. Ограничение maxOptions
  9. Отображение результатов
  10. Срабатывание событий onSearch / onDropdownOpen