Настройка алгоритма поиска

Поиск в библиотеке Choices.js предназначен для быстрого нахождения элементов внутри списка <select> или набора пользовательских значений. По умолчанию библиотека автоматически фильтрует варианты по введённому тексту, однако механизм поиска можно гибко настраивать.

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

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

Базовое включение поиска

Поиск активируется автоматически для большинства элементов, однако его можно явно включить через параметр searchEnabled.

const choices = new Choices('#countries', {
  searchEnabled: true
});

Если установить false, поле поиска исчезнет.

const choices = new Choices('#countries', {
  searchEnabled: false
});

Это особенно полезно:

  • для коротких списков;
  • мобильных интерфейсов;
  • фиксированных наборов значений;
  • минималистичных UI-компонентов.

Настройка минимального количества символов

Параметр searchFloor задаёт минимальное число символов, после которого начинается поиск.

const choices = new Choices('#cities', {
  searchFloor: 3
});

Поведение:

Введено Поиск
a нет
ab нет
abc да

Такой подход снижает:

  • количество операций фильтрации;
  • нагрузку на DOM;
  • число ненужных совпадений.

Особенно полезно при работе с тысячами элементов.


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

Параметр searchResultLimit задаёт максимальное число отображаемых совпадений.

const choices = new Choices('#users', {
  searchResultLimit: 10
});

Если найдено 500 совпадений, интерфейс покажет только первые 10.

Преимущества:

  • уменьшение нагрузки на рендеринг;
  • повышение отзывчивости;
  • предотвращение длинных выпадающих списков.

Поиск по нескольким полям

Choices.js поддерживает поиск не только по label, но и по дополнительным полям объекта.

Пример данных:

const data = [
  {
    value: '1',
    label: 'JavaScript',
    customProperties: {
      category: 'Frontend',
      level: 'Advanced'
    }
  },
  {
    value: '2',
    label: 'PHP',
    customProperties: {
      category: 'Backend',
      level: 'Intermediate'
    }
  }
];

Настройка поиска:

const choices = new Choices('#skills', {
  choices: data,
  searchFields: [
    'label',
    'value',
    'customProperties.category',
    'customProperties.level'
  ]
});

Теперь поиск будет учитывать:

  • название;
  • значение;
  • категорию;
  • уровень.

Поле searchFields

Параметр searchFields является центральным механизмом настройки алгоритма поиска.

Пример:

searchFields: ['label']

Поиск только по отображаемому тексту.


Поиск по value

searchFields: ['value']

Полезно при:

  • поиске по идентификаторам;
  • внутренних кодах;
  • SKU;
  • артикулах.

Поиск по нескольким источникам

searchFields: [
  'label',
  'customProperties.description',
  'customProperties.tags'
]

Такой подход превращает Choices.js в полноценный клиентский поисковый интерфейс.


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

Параметр searchChoices управляет самим поиском, но регистр контролируется внутренним алгоритмом библиотеки.

По умолчанию поиск нечувствителен к регистру.

JavaScript
javascript
JAVASCRIPT

Все варианты будут найдены одинаково.

Это реализуется внутренним преобразованием строк в нижний регистр.


Поиск по подстроке

Стандартный алгоритм Choices.js использует поиск по вхождению строки.

Пример:

Значение Запрос Совпадение
JavaScript script да
TypeScript script да
Python script нет

Такой механизм удобен для:

  • больших каталогов;
  • списков технологий;
  • справочников;
  • систем тегов.

Сортировка результатов поиска

Параметр shouldSort отвечает за сортировку элементов.

const choices = new Choices('#languages', {
  shouldSort: true
});

По умолчанию библиотека сортирует результаты алфавитно.


Отключение сортировки

const choices = new Choices('#languages', {
  shouldSort: false
});

Это важно, если:

  • порядок приходит с сервера;
  • элементы отсортированы по популярности;
  • используется приоритетная выдача;
  • требуется сохранение оригинального порядка.

Пользовательская сортировка результатов

Choices.js позволяет полностью переопределить алгоритм сортировки.

const choices = new Choices('#products', {
  sorter: function(a, b) {
    return b.score - a.score;
  }
});

Пример сортировки по рейтингу:

const choices = new Choices('#products', {
  sorter: (a, b) => {
    return b.customProperties.rating - a.customProperties.rating;
  }
});

Использование Fuse.js для расширенного поиска

Для сложных сценариев часто подключается Fuse.js.

Fuse.js предоставляет:

  • fuzzy search;
  • поиск с ошибками;
  • ранжирование совпадений;
  • взвешивание полей;
  • интеллектуальную релевантность.

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

const fuse = new Fuse(data, {
  keys: ['label', 'customProperties.description'],
  threshold: 0.3
});

input.addEventListener('input', (e) => {
  const results = fuse.search(e.target.value);

  choices.clearChoices();

  choices.setChoices(
    results.map(item => item.item),
    'value',
    'label',
    true
  );
});

Нечёткий поиск

Нечёткий поиск позволяет находить значения даже с опечатками.

Пример:

Запрос Совпадение
javscrit JavaScript
pyton Python
reasct React

Стандартный поиск Choices.js этого не поддерживает, поэтому используются внешние библиотеки.


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

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


Ограничение количества элементов

Если список содержит тысячи записей:

searchResultLimit: 20

Увеличение searchFloor

searchFloor: 3

Позволяет избежать фильтрации при коротких запросах.


Отложенная загрузка

Вместо хранения 50 000 элементов в браузере:

input.addEventListener('search', async (event) => {
  const query = event.detail.value;

  const response = await fetch(`/api/search?q=${query}`);
  const data = await response.json();

  choices.clearChoices();

  choices.setChoices(data, 'value', 'label', true);
});

Такой подход уменьшает:

  • потребление памяти;
  • размер DOM;
  • время инициализации.

Серверный поиск

Choices.js можно использовать как оболочку над серверным API.

Пример:

const choices = new Choices('#users');

document.querySelector('#users')
  .addEventListener('search', async (event) => {

    const value = event.detail.value;

    if (value.length < 2) {
      return;
    }

    const response = await fetch(`/users?q=${value}`);
    const users = await response.json();

    choices.clearChoices();

    choices.setChoices(users, 'id', 'name', true);
  });

Преимущества серверного поиска:

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

Дебаунс поиска

При поиске через API необходимо ограничивать количество запросов.

Пример debounce:

function debounce(fn, delay) {
  let timer;

  return function(...args) {
    clearTimeout(timer);

    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

Использование:

const searchHandler = debounce(async (event) => {
  const query = event.detail.value;

  const response = await fetch(`/search?q=${query}`);
  const data = await response.json();

  choices.clearChoices();

  choices.setChoices(data, 'value', 'label', true);

}, 300);

element.addEventListener('search', searchHandler);

Кастомная логика фильтрации

Иногда требуется полностью заменить стандартный поиск.

Пример:

const items = [
  'JavaScript',
  'TypeScript',
  'Python',
  'PHP'
];

function customSearch(query) {
  return items.filter(item => {
    return item.startsWith(query);
  });
}

Поиск только по началу строки:

console.log(customSearch('Py'));

Результат:

['Python']

Поиск с приоритетами

Часто требуется ранжировать результаты.

Пример:

function rankResults(query, items) {

  return items.sort((a, b) => {

    const aStarts = a.label.startsWith(query);
    const bStarts = b.label.startsWith(query);

    if (aStarts && !bStarts) {
      return -1;
    }

    if (!aStarts && bStarts) {
      return 1;
    }

    return 0;
  });
}

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


Работа с пользовательскими событиями поиска

Choices.js генерирует событие search.

Пример:

element.addEventListener('search', (event) => {
  console.log(event.detail.value);
});

Структура события:

{
  value: 'jav'
}

Это позволяет:

  • отправлять запросы на сервер;
  • логировать поисковые запросы;
  • реализовывать аналитику;
  • подключать внешние алгоритмы.

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

После изменения источника данных полезно очищать старые результаты.

choices.clearChoices();

Полная перезагрузка:

choices.clearStore();

Разница:

Метод Что очищает
clearChoices() список вариантов
clearStore() внутреннее состояние полностью

Поиск по тегам

При использовании removeItemButton и режима множественного выбора поиск работает и по выбранным элементам.

const choices = new Choices('#tags', {
  removeItemButton: true
});

Это удобно для:

  • теговых систем;
  • категорий;
  • фильтрации контента;
  • выбора навыков.

Асинхронный поиск

Choices.js хорошо сочетается с асинхронными источниками данных.

Пример:

async function loadUsers(query) {

  const response = await fetch(`/api/users?q=${query}`);

  return await response.json();
}

Интеграция:

element.addEventListener('search', async (event) => {

  const query = event.detail.value;

  const users = await loadUsers(query);

  choices.clearChoices();

  choices.setChoices(users, 'id', 'name', true);
});

Обработка пустых результатов

Параметр noResultsText задаёт сообщение при отсутствии совпадений.

const choices = new Choices('#users', {
  noResultsText: 'Ничего не найдено'
});

Локализация поиска

Choices.js поддерживает настройку всех текстов интерфейса.

Пример:

const choices = new Choices('#countries', {
  searchPlaceholderValue: 'Поиск страны',
  noResultsText: 'Совпадений нет',
  noChoicesText: 'Список пуст'
});

Оптимизация больших списков

Для массивов свыше 10 000 элементов рекомендуется:

  1. Использовать серверный поиск.
  2. Ограничивать результаты.
  3. Увеличивать searchFloor.
  4. Отключать сортировку.
  5. Применять debounce.
  6. Использовать виртуализацию.
  7. Не хранить лишние customProperties.

Типичные проблемы поиска

Поиск ничего не находит

Причины:

  • неправильный searchFields;
  • пустые значения;
  • отключён searchEnabled;
  • данные не загружены.

Проверка:

console.log(choices._store);

Медленный интерфейс

Причины:

  • слишком большой DOM;
  • отсутствие debounce;
  • сложная сортировка;
  • поиск по множеству полей.

Некорректная сортировка

Причина часто связана с:

shouldSort: true

или пользовательским sorter.


Комбинирование нескольких стратегий поиска

Крупные приложения часто объединяют:

  • локальный поиск;
  • серверную фильтрацию;
  • приоритетную выдачу;
  • fuzzy search;
  • кэширование результатов.

Пример архитектуры:

search
  → debounce
  → API
  → Fuse.js
  → сортировка
  → setChoices()

Такой подход позволяет создавать:

  • autocomplete;
  • интеллектуальные селекты;
  • поисковые панели;
  • интерфейсы выбора пользователей;
  • каталоги товаров;
  • системы тегирования.