При разработке сложных интерфейсов на управляемых формах часто возникает задача отображения данных, которые имеют четкую иерархию, но при этом не хранятся в базе данных как справочники. Это могут быть результаты сложных расчетов, данные из внешних систем (API) или структуры, сформированные динамически «на лету». Основная сложность здесь заключается в реализации поиска: пользователю важно не просто найти строку, а увидеть её в контексте иерархии — вместе со всеми родительскими узлами. В этой статье мы детально разберем, как построить эффективный механизм поиска для таких данных.
Рассмотрим стандартные механизмы 1С. Динамический список (ДинамическийСписок) — мощный инструмент, который «из коробки» поддерживает иерархию, поиск и отборы. Однако у него есть критическое ограничение: иерархия в динамических списках работает только для объектов метаданных, имеющих ссылки (СправочникСсылка, ПланВидовХарактеристикСсылка). Если данные представляют собой таблицу строк, сформированную программно, динамический список не сможет отобразить их в виде дерева.
Единственным подходящим инструментом остается ДеревоЗначений. Но здесь мы сталкиваемся с двумя проблемами:
Проанализируем оптимальный алгоритм решения этой задачи.
Рассмотрим наиболее надежный подход, который позволяет реализовать полноценную фильтрацию. Суть его заключается в том, что мы разделяем хранение данных и их отображение на форме.
Шаг 1. Подготовка эталонных данных.
Создадим скрытый реквизит формы (или переменную в модуле), где будут храниться полные данные в виде индексированной ТаблицыЗначений. Колонки должны включать Имя, Родитель (имя родителя) и другие необходимые данные. Индексация колонки Родитель и колонки поиска критически важна для скорости работы на сервере.
Шаг 2. Создание дерева отображения.
На форме разместим реквизит с типом ДеревоЗначений. Именно его будет видеть пользователь. При открытии формы или при пустом поиске это дерево заполняется всеми данными.
Шаг 3. Алгоритм фильтрации при поиске.
Когда пользователь вводит текст в строку поиска, мы не пытаемся «удалять» строки из дерева. Вместо этого мы строим новое дерево результатов «снизу вверх».
Разберем, как правильно сформировать дерево результатов, чтобы сохранить иерархию. Выясним причину, почему это лучше делать именно так: если мы будем просто искать элементы и добавлять их в дерево, мы потеряем информацию о том, в какой «папке» они лежат.
Алгоритм действий следующий:
Найти() или Подобно), мы помечаем её как необходимую для вывода.Рассмотрим пример кода для серверной части обработки поиска:
&НаСервере
Процедура ВыполнитьПоискНаСервере(ТекстПоиска)
// Очищаем текущее дерево формы
ДеревоРезультат = РеквизитФормыВЗначение("ДеревоНаФорме");
ДеревоРезультат.Строки.Очистить();
Если ПустаяСтрока(ТекстПоиска) Тогда
// Заполняем дерево всеми данными из эталонной таблицы
ЗаполнитьДеревоПолностью(ДеревоРезультат);
Иначе
// Текст поиска приводим к нижнему регистру для универсальности
ТекстПоиска = НРег(ТекстПоиска);
// Массив для хранения строк, которые точно должны попасть в результат
ПодходящиеСтроки = ЭталоннаяТаблица.НайтиСтроки(Новый Структура("Имя", ТекстПоиска)); // Это упрощенный пример
// Более гибкий поиск через цикл:
Для Каждого СтрокаДанных Из ЭталоннаяТаблица Цикл
Если Найти(НРег(СтрокаДанных.Имя), ТекстПоиска) > 0 Тогда
// Добавляем строку и всю цепочку родителей в дерево результата
ДобавитьСтрокуСРодителями(ДеревоРезультат, СтрокаДанных);
КонецЕсли;
КонецЦикла;
КонецЕсли;
ЗначениеВРеквизитФормы(ДеревоРезультат, "ДеревоНаФорме");
КонецПроцедуры
Проанализируем ситуацию, когда дерево содержит более 5000 строк. Передача такого объема данных между сервером и клиентом при каждом вводе символа может привести к «замиранию» интерфейса. Посмотрим на способы оптимизации:
1. Задержка начала поиска (Debouncing).
Не стоит запускать поиск мгновенно при изменении текста. Используйте обработчик ожидания. Если пользователь печатает быстро, поиск должен начаться только через 500–700 миллисекунд после ввода последнего символа.
2. Ограничение вложенности и количества строк.
Если результат поиска превышает, скажем, 1000 строк, имеет смысл выдать пользователю предупреждение: «Слишком много результатов, уточните запрос». Это спасет браузер или тонкий клиент от падения при отрисовке огромного дерева.
3. Использование индексации.
При поиске родителей в большой таблице значений обязательно используйте предварительно проиндексированные колонки. Это превращает поиск из линейного (медленного) в практически мгновенный.
Если иерархия не слишком глубокая, рассмотрите вариант использования обычного списка вместо дерева. Чтобы визуально сохранить структуру, можно программно добавлять неразрывные пробелы или специальные символы перед текстом в колонке «Имя».
Например:
Такой подход позволяет использовать стандартный ДинамическийСписок с его мощными механизмами поиска, но лишает пользователя возможности сворачивать узлы. Выясним, что важнее для вашей задачи: интерактивность (сворачивание) или скорость поиска.
Разберем по шагам финальную схему реализации качественного поиска в дереве:
Применение паттерна «Двух деревьев» гарантирует, что пользователь получит быстрый и интуитивно понятный интерфейс даже при работе с данными, которые не имеют жесткой привязки к справочникам системы 1С.