Индексация в Tom Select строится вокруг подготовки набора данных для быстрого поиска и ранжирования опций. Библиотека не использует полноценный полнотекстовый движок, но реализует собственный индексатор, оптимизированный под интерактивный UI: минимальная задержка, инкрементальные пересчёты, предсказуемое ранжирование.
Основная идея заключается в том, что каждая опция преобразуется в поисковый профиль — набор нормализованных строковых представлений, по которым затем выполняется сопоставление с пользовательским запросом.
Каждая опция внутри Tom Select проходит этап преобразования в внутренний индексируемый объект:
text — основное отображаемое значениеvalue — идентификаторsearch — набор строк, по которым выполняется поискscore — числовая метрика релевантностиtokens — разложенные части строки (при расширенной
конфигурации)Пример базовой структуры:
{
value: "1",
text: "San Francisco",
$order: 1,
score: 0,
search: "san francisco"
}
Именно поле search становится ключевым элементом
индексации. Оно формируется либо автоматически, либо через
render/load/options.
Перед добавлением в индекс каждая строка проходит нормализацию:
Внутренний процесс можно представить так:
function normalize(str) {
return str
.toLowerCase()
.trim()
.normalize("NFD")
.replace(/\p{Diacritic}/gu, "");
}
Нормализация критична для предсказуемого поиска, особенно при мультиязычных данных.
Tom Select не строит отдельную структуру вроде inverted index, но формирует вычисляемый индекс на лету.
Алгоритм подготовки:
optionssearchУпрощённая модель:
index = options.map(opt => ({
value: opt.value,
text: opt.text,
search: normalize(opt.text + " " + (opt.search || "")),
}));
При вводе строки пользовательский запрос также нормализуется:
query = normalize(input);
Далее выполняется сравнение с каждой опцией:
Простейшая модель:
function match(option, query) {
return option.search.indexOf(query) !== -1;
}
Однако в реальной реализации добавляется ранжирование.
Каждому совпадению присваивается score, который влияет
на порядок отображения.
Факторы, влияющие на score:
Пример логики:
function scoreOption(option, query) {
const pos = option.search.indexOf(query);
if (pos === 0) return 100;
if (pos > 0) return 50;
return 0;
}
Далее результаты сортируются:
results.sort((a, b) => b.score - a.score);
При использовании load(query, callback) индекс не
строится заранее. Вместо этого:
Пример:
new TomSelect("#select", {
load: function(query, callback) {
fetch("/search?q=" + encodeURIComponent(query))
.then(res => res.json())
.then(data => callback(data));
}
});
В этом режиме индексация частично переносится на сервер, что уменьшает нагрузку на клиент.
При использовании addOption или addOptions
новые элементы сразу включаются в индекс:
select.addOption({
value: "tokyo",
text: "Tokyo"
});
После добавления происходит:
Поле searchField позволяет управлять тем, какие части
объекта участвуют в индексации:
new TomSelect("#select", {
searchField: ["text", "code", "country"]
});
В этом случае индекс строится не только по text, но и по
дополнительным полям:
search = normalize(
opt.text + " " +
opt.code + " " +
opt.country
);
Это расширяет поисковую поверхность и позволяет реализовать поведение, близкое к фасетному поиску.
Tom Select позволяет контролировать поведение индексации через параметры:
ignoreCase — отключение/включение
регистрозависимостиscore — пользовательская функция ранжированияsortField — порядок сортировки результатовПример кастомного ранжирования:
new TomSelect("#select", {
score: function(search) {
return function(option) {
if (option.text === search) return 100;
if (option.text.startsWith(search)) return 75;
if (option.text.includes(search)) return 25;
return 0;
};
}
});
Здесь индекс становится полностью управляемым.
При работе с объектами сложной структуры индексация часто требует явного указания пути:
{
value: 1,
name: {
en: "London",
ru: "Лондон"
}
}
Индексация:
searchField: ["name.en", "name.ru"]
Tom Select извлекает значения через обращение к свойствам объекта и объединяет их в общий индекс.
Основные факторы, влияющие на скорость:
optionssearchFieldscoreОптимизации:
searchField до минимально необходимых
полейВнутри механизма поиска используется простая форма кэширования:
Модель:
cache[query] = results;
При повторном вводе одинакового запроса происходит возврат без пересчёта индекса.
При изменении данных индекс не пересобирается полностью, а обновляется частично:
valueЭто позволяет сохранять отзывчивость интерфейса даже при большом объёме данных.
В режиме create: true индексация расширяется за счёт
пользовательских значений:
new TomSelect("#select", {
create: true
});
Новые значения автоматически:
optionsЭто превращает индекс в динамически расширяемую структуру.
При отсутствии поисковой строки:
optionssortFieldЭто важное отличие от полнотекстовых систем, где пустой запрос часто требует отдельной обработки ранжирования.
Подход Tom Select имеет ряд структурных ограничений:
Эти ограничения компенсируются простотой архитектуры и предсказуемым поведением в UI-контексте.
При необходимости можно полностью заменить механизм поиска:
new TomSelect("#select", {
score: function() {
return function() {
return 1;
};
}
});
Или внедрить внешние библиотеки:
В этом случае Tom Select остаётся только слоем визуализации, а индексирование переносится наружу.