Регистрозависимый поиск

При работе с библиотекой Choices.js поведение поиска по умолчанию реализовано через Fuse.js и ориентировано на нечувствительное к регистру сопоставление строк. Это означает, что значения Apple, apple и APPLE рассматриваются как эквивалентные при фильтрации списка. В ряде сценариев требуется противоположное поведение — строгая регистрозависимость, при которой различие между заглавными и строчными буквами влияет на результаты поиска.


Внутри Choices.js поиск основан на Fuse.js, который по умолчанию приводит строки к нормализованному виду:

  • регистр игнорируется
  • выполняется частичное сопоставление
  • учитываются заданные поля searchFields

Пример стандартного поведения:

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

При таком подходе поиск строки apple вернёт:

  • Apple
  • apple pie
  • Green Apple

без различий по регистру.


Причины необходимости регистрозависимого поиска

Регистрозависимая фильтрация используется в случаях:

  • различение технических идентификаторов (API, Api, api)
  • работа с кодами продуктов (ABc123abc123)
  • корпоративные справочники с формализованными обозначениями
  • языковые модели, где регистр несёт смысловую нагрузку

Ограничения стандартного Fuse.js в Choices.js

Fuse.js, используемый внутри Choices.js, изначально оптимизирован для человеко-ориентированного поиска. Поэтому:

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

Включение регистрозависимого поведения через fuseOptions

Основной способ добиться строгого сравнения — настройка Fuse.js через fuseOptions.

const choices = new Choices('#select', {
  searchEnabled: true,
  searchChoices: true,
  fuseOptions: {
    ignoreLocation: true,
    threshold: 0.0,
    ignoreFieldNorm: true,
    minMatchCharLength: 1,
    isCaseSensitive: true
  }
});

Ключевые параметры

isCaseSensitive

  • управляет чувствительностью к регистру
  • при true различаются A и a

threshold

  • минимизирует «размытость» совпадений
  • 0.0 превращает поиск в почти точное совпадение

ignoreLocation

  • отключает влияние позиции совпадения в строке

Альтернативный подход: полное переопределение фильтра поиска

Если поведение Fuse.js недостаточно предсказуемо, используется кастомная логика фильтрации.

В Choices.js можно переопределить механизм поиска через callbackOnCreateTemplates и внутреннюю фильтрацию данных, либо через предварительную нормализацию списка.

Пример ручного фильтра

const choices = new Choices('#select', {
  searchEnabled: true,
  searchChoices: true,
  callbackOnSearch: function(value, choice) {
    return choice.value.includes(value);
  }
});

Здесь используется стандартная строковая проверка includes, которая уже является регистрозависимой.


Принудительная регистрозависимость через нормализацию данных

Дополнительный способ — отказ от автоматической нормализации входных данных.

const items = [
  { value: 'Apple', label: 'Apple' },
  { value: 'apple', label: 'apple' }
];

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


Использование кастомного search-поля

Choices позволяет ограничить поля поиска через searchFields, что влияет на поведение фильтрации:

const choices = new Choices('#select', {
  searchEnabled: true,
  searchChoices: true,
  searchFields: ['label', 'value']
});

При комбинировании с кастомным Fuse можно добиться строгого контроля над тем, какие именно строки участвуют в сравнении.


Комбинированная стратегия для строгого поиска

Для сложных систем применяется гибридный подход:

  • отключение «размытого» поиска
  • минимизация threshold
  • включение case-sensitive режима Fuse
  • использование точного сравнения строк на уровне callback
const choices = new Choices('#select', {
  searchEnabled: true,
  searchChoices: true,
  fuseOptions: {
    threshold: 0,
    ignoreLocation: true,
    ignoreFieldNorm: true,
    isCaseSensitive: true
  },
  callbackOnSearch: (value, choice) => {
    return choice.label.indexOf(value) !== -1;
  }
});

Поведение при частичном совпадении

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

  • App не совпадает с app
  • Apple не совпадает с apple
  • APP совпадает только с точным APP

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


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

При отключении fuzzy-логики в Choices.js поведение становится более линейным:

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

Типичные ошибки при настройке регистрозависимости

Смешивание Fuse и ручной фильтрации

Одновременное использование fuseOptions и callbackOnSearch может приводить к неожиданным результатам, так как сначала применяется Fuse, затем кастомная логика.

Игнорирование нормализации данных

Если данные заранее приведены к нижнему регистру, включение case-sensitive режима не даёт эффекта.

Неправильное ожидание полного отключения Fuse

Choices не позволяет полностью удалить Fuse без кастомной переработки поискового механизма.


Архитектурный подход к строгому поиску

В системах, где регистр критичен, Choices.js обычно используется не как единственный слой логики, а как UI-компонент:

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

Такой подход исключает зависимость от внутреннего поведения Fuse.js и делает регистрозависимость предсказуемой.