В Tom Select механизм поиска изначально рассчитан на работу не только
с одиночным текстовым полем, но и с набором атрибутов объекта. Ключевым
параметром выступает searchField, который принимает массив
строк с именами полей данных, по которым выполняется индексирование и
последующий поиск.
new TomSelect("#select", {
valueField: "id",
labelField: "title",
searchField: ["title", "description", "tags"],
options: [
{ id: 1, title: "JavaScript", description: "Язык программирования", tags: "js,frontend" },
{ id: 2, title: "TypeScript", description: "Надстройка над JS", tags: "ts,typing" }
]
});
При такой конфигурации индекс строится сразу по трём полям, что позволяет одному запросу совпадать с различными частями объекта. Внутренний алгоритм разбивает строки на токены и сравнивает их с вводом пользователя, формируя базовую релевантность.
При использовании массива searchField Tom Select
объединяет значения всех указанных свойств объекта в единый поисковый
индекс. Однако логика сравнения не является простым конкатенированием
строк — каждое поле участвует в вычислении релевантности отдельно.
Если совпадение найдено в нескольких полях одного объекта, итоговый score увеличивается. Это создаёт естественное ранжирование:
title даёт максимальный весdescription — среднийtags — минимальный, если не
переопределеноТакое поведение можно считать неявной многофакторной моделью ранжирования, где приоритет зависит от структуры данных.
Для тонкого контроля над многопольным поиском используется функция
score. Она позволяет переопределить вклад каждого поля в
итоговую релевантность.
new TomSelect("#select", {
valueField: "id",
labelField: "title",
searchField: ["title", "description", "tags"],
score: function(search) {
return function(item) {
let score = 0;
if (item.title.toLowerCase().includes(search.toLowerCase())) {
score += 10;
}
if (item.description.toLowerCase().includes(search.toLowerCase())) {
score += 5;
}
if (item.tags.toLowerCase().includes(search.toLowerCase())) {
score += 2;
}
return score;
};
}
});
В этой модели поиск перестаёт быть симметричным. Поля начинают играть роль независимых источников сигнала, а итоговая релевантность становится функцией взвешенной суммы.
Во многих прикладных сценариях данные не являются плоскими. Объекты могут содержать вложенные структуры:
{
id: 1,
title: "React",
meta: {
description: "UI библиотека",
keywords: ["frontend", "ui", "components"]
}
}
Tom Select не индексирует вложенные поля автоматически, поэтому требуется предварительная нормализация данных.
function normalizeOptions(data) {
return data.map(item => ({
...item,
search_blob: [
item.title,
item.meta?.description,
(item.meta?.keywords || []).join(" ")
].join(" ")
}));
}
Далее поиск строится по одному агрегированному полю:
new TomSelect("#select", {
valueField: "id",
labelField: "title",
searchField: ["search_blob"],
options: normalizeOptions(data)
});
Такой подход снижает гибкость, но повышает предсказуемость и производительность индексации.
Встроенный поиск Tom Select приводит строки к нижнему регистру,
однако поведение при работе с диакритическими символами (например,
é, ö, я) зависит от окружения и
браузерной реализации сравнения строк.
Для унификации данных часто применяется нормализация Unicode:
function normalizeString(str) {
return str
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
}
Использование такой функции особенно важно при мультиязычных наборах данных, где одно и то же слово может иметь разные формы записи.
Параметр filter позволяет полностью переопределить
логику включения элемента в результаты поиска.
new TomSelect("#select", {
valueField: "id",
labelField: "title",
searchField: ["title", "description"],
filter: function(item, search) {
const s = search.toLowerCase();
return (
item.title.toLowerCase().includes(s) ||
item.description.toLowerCase().includes(s)
);
}
});
В этом режиме searchField фактически перестаёт играть
роль основного механизма и становится вспомогательной структурой для
UI-подсказок, тогда как фильтрация полностью контролируется
разработчиком.
На практике часто применяется гибридная модель:
searchField отвечает за индексированиеscore управляет ранжированиемfilter определяет допустимость результатаnew TomSelect("#select", {
valueField: "id",
labelField: "title",
searchField: ["title", "description", "tags"],
score: function(search) {
return item => {
let score = 0;
if (item.title.includes(search)) score += 20;
if (item.description.includes(search)) score += 10;
if (item.tags.includes(search)) score += 5;
return score;
};
},
filter: function(item, search) {
return item.title.length > 0;
}
});
Такой подход позволяет разделить ответственность между этапами обработки данных: фильтрация исключает лишнее, score сортирует, searchField формирует базовый индекс.
Более формализованный подход заключается в использовании коэффициентов веса:
const weights = {
title: 5,
description: 3,
tags: 1
};
new TomSelect("#select", {
searchField: ["title", "description", "tags"],
score: function(search) {
const s = search.toLowerCase();
return item => {
let score = 0;
if (item.title.toLowerCase().includes(s)) {
score += weights.title;
}
if (item.description.toLowerCase().includes(s)) {
score += weights.description;
}
if (item.tags.toLowerCase().includes(s)) {
score += weights.tags;
}
return score;
};
}
});
Такая модель делает поведение поиска предсказуемым и легко масштабируемым при добавлении новых полей.
Рост числа searchField напрямую влияет на стоимость
построения индекса и время обработки запроса. Особенно это заметно при
тысячах опций.
Ключевые факторы влияния:
scoreОптимизационные практики включают:
scoreКогда поле представляет собой массив, его необходимо преобразовать в строку или использовать специализированную логику сравнения:
{
id: 1,
title: "Vue",
tags: ["frontend", "framework", "reactive"]
}
Решение:
searchField: ["title", "tags_blob"],
options: data.map(item => ({
...item,
tags_blob: item.tags.join(" ")
}))
Такой подход позволяет сохранить простоту стандартного поиска, не усложняя внутренний алгоритм библиотеки.
Многопольный поиск в Tom Select по сути формирует контекстную модель релевантности, где каждая часть объекта влияет на итоговое положение в списке результатов.
При расширении числа полей важно учитывать:
Рациональная структура данных обычно предполагает наличие одного
главного поля (например, title) и нескольких
вспомогательных (description, tags), где
влияние вторых строго ограничено.