Как реализовать иерархический поиск в дереве значений на управляемой форме 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке сложных интерфейсов на управляемых формах часто возникает задача отображения данных, которые имеют четкую иерархию, но при этом не хранятся в базе данных как справочники. Это могут быть результаты сложных расчетов, данные из внешних систем (API) или структуры, сформированные динамически «на лету». Основная сложность здесь заключается в реализации поиска: пользователю важно не просто найти строку, а увидеть её в контексте иерархии — вместе со всеми родительскими узлами. В этой статье мы детально разберем, как построить эффективный механизм поиска для таких данных.

Анализ проблемы: Динамический список или Дерево значений?

Рассмотрим стандартные механизмы 1С. Динамический список (ДинамическийСписок) — мощный инструмент, который «из коробки» поддерживает иерархию, поиск и отборы. Однако у него есть критическое ограничение: иерархия в динамических списках работает только для объектов метаданных, имеющих ссылки (СправочникСсылка, ПланВидовХарактеристикСсылка). Если данные представляют собой таблицу строк, сформированную программно, динамический список не сможет отобразить их в виде дерева.

Единственным подходящим инструментом остается ДеревоЗначений. Но здесь мы сталкиваемся с двумя проблемами:

  1. Производительность: Деревья на управляемых формах работают медленнее таблиц, особенно если в них тысячи строк.
  2. Отсутствие встроенной фильтрации: Стандартный поиск в таблице формы (начиная с платформы 8.3.16) подсвечивает найденное, но не скрывает лишние ветки, что неудобно при больших объемах данных.

Проанализируем оптимальный алгоритм решения этой задачи.

Метод «Двух деревьев»: Паттерн разделения данных и представления

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

Шаг 1. Подготовка эталонных данных.
Создадим скрытый реквизит формы (или переменную в модуле), где будут храниться полные данные в виде индексированной ТаблицыЗначений. Колонки должны включать Имя, Родитель (имя родителя) и другие необходимые данные. Индексация колонки Родитель и колонки поиска критически важна для скорости работы на сервере.

Шаг 2. Создание дерева отображения.
На форме разместим реквизит с типом ДеревоЗначений. Именно его будет видеть пользователь. При открытии формы или при пустом поиске это дерево заполняется всеми данными.

Шаг 3. Алгоритм фильтрации при поиске.
Когда пользователь вводит текст в строку поиска, мы не пытаемся «удалять» строки из дерева. Вместо этого мы строим новое дерево результатов «снизу вверх».

Реализация алгоритма поиска «Снизу вверх»

Разберем, как правильно сформировать дерево результатов, чтобы сохранить иерархию. Выясним причину, почему это лучше делать именно так: если мы будем просто искать элементы и добавлять их в дерево, мы потеряем информацию о том, в какой «папке» они лежат.

Алгоритм действий следующий:

  1. Очищаем дерево, отображаемое на форме.
  2. Проходим циклом по эталонной таблице значений.
  3. Если строка соответствует условию поиска (функция Найти() или Подобно), мы помечаем её как необходимую для вывода.
  4. Для каждой найденной строки рекурсивно ищем её родителей в эталонной таблице до самого верхнего уровня.
  5. Все найденные элементы (целевой элемент + все его предки) переносим в дерево отображения, соблюдая структуру.

Рассмотрим пример кода для серверной части обработки поиска:


&НаСервере
Процедура ВыполнитьПоискНаСервере(ТекстПоиска)
    
    // Очищаем текущее дерево формы
    ДеревоРезультат = РеквизитФормыВЗначение("ДеревоНаФорме");
    ДеревоРезультат.Строки.Очистить();
    
    Если ПустаяСтрока(ТекстПоиска) Тогда
        // Заполняем дерево всеми данными из эталонной таблицы
        ЗаполнитьДеревоПолностью(ДеревоРезультат);
    Иначе
        // Текст поиска приводим к нижнему регистру для универсальности
        ТекстПоиска = НРег(ТекстПоиска);
        
        // Массив для хранения строк, которые точно должны попасть в результат
        ПодходящиеСтроки = ЭталоннаяТаблица.НайтиСтроки(Новый Структура("Имя", ТекстПоиска)); // Это упрощенный пример
        
        // Более гибкий поиск через цикл:
        Для Каждого СтрокаДанных Из ЭталоннаяТаблица Цикл
            Если Найти(НРег(СтрокаДанных.Имя), ТекстПоиска) > 0 Тогда
                // Добавляем строку и всю цепочку родителей в дерево результата
                ДобавитьСтрокуСРодителями(ДеревоРезультат, СтрокаДанных);
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    
    ЗначениеВРеквизитФормы(ДеревоРезультат, "ДеревоНаФорме");
    
КонецПроцедуры

Оптимизация производительности при больших объемах

Проанализируем ситуацию, когда дерево содержит более 5000 строк. Передача такого объема данных между сервером и клиентом при каждом вводе символа может привести к «замиранию» интерфейса. Посмотрим на способы оптимизации:

1. Задержка начала поиска (Debouncing).
Не стоит запускать поиск мгновенно при изменении текста. Используйте обработчик ожидания. Если пользователь печатает быстро, поиск должен начаться только через 500–700 миллисекунд после ввода последнего символа.

2. Ограничение вложенности и количества строк.
Если результат поиска превышает, скажем, 1000 строк, имеет смысл выдать пользователю предупреждение: «Слишком много результатов, уточните запрос». Это спасет браузер или тонкий клиент от падения при отрисовке огромного дерева.

3. Использование индексации.
При поиске родителей в большой таблице значений обязательно используйте предварительно проиндексированные колонки. Это превращает поиск из линейного (медленного) в практически мгновенный.

Альтернативный вариант: Имитация иерархии в списке

Если иерархия не слишком глубокая, рассмотрите вариант использования обычного списка вместо дерева. Чтобы визуально сохранить структуру, можно программно добавлять неразрывные пробелы или специальные символы перед текстом в колонке «Имя».

Например:

Такой подход позволяет использовать стандартный ДинамическийСписок с его мощными механизмами поиска, но лишает пользователя возможности сворачивать узлы. Выясним, что важнее для вашей задачи: интерактивность (сворачивание) или скорость поиска.

Подводим итоги

Разберем по шагам финальную схему реализации качественного поиска в дереве:

  1. Храним данные на сервере в проиндексированной таблице значений.
  2. Реализуем процедуру поиска, которая строит дерево «снизу вверх», включая всех родителей найденного элемента.
  3. Оптимизируем вызов этой процедуры через обработчик ожидания на клиенте, чтобы не перегружать сервер частыми запросами.
  4. Используем возможности платформы 8.3.16+ только для визуальной подсветки, но для полноценной фильтрации применяем программный метод пересчета строк.

Применение паттерна «Двух деревьев» гарантирует, что пользователь получит быстрый и интуитивно понятный интерфейс даже при работе с данными, которые не имеют жесткой привязки к справочникам системы 1С.

← На главную