Работа с иерархическими структурами данных, такими как ДеревоЗначений, всегда сопряжена с определенными трудностями при реализации поиска или отбора. В отличие от плоских таблиц, в дереве недостаточно просто скрыть строку — необходимо учитывать связь между родительскими и дочерними узлами. Если дочерний элемент соответствует критериям поиска, то и все его родители должны остаться видимыми, чтобы дерево не "рассыпалось".
В этой статье мы подробно разберем несколько проверенных методов реализации отбора в дереве значений, проанализируем их плюсы и минусы, а также изучим конкретные примеры кода для каждого случая.
Этот способ считается классическим и наиболее гибким. Его суть заключается в том, что мы создаем копию исходного дерева или работаем с оригиналом, удаляя строки, не соответствующие условию. Рассмотрим подробнее алгоритм «Снизу Вверх», который позволяет сохранить целостность иерархии.
Почему именно снизу вверх? Если мы начнем удалять родителей сразу, мы потеряем доступ к их детям. Если же мы сначала проверим «листья» (самый нижний уровень), мы сможем определить, нужно ли оставлять родителя.
Проанализируем пример функции, которая рекурсивно очищает дерево от строк, не подходящих под условие:
Процедура ФильтроватьДерево(СтрокиДерева, ЗначениеПоиска)
МассивНаУдаление = Новый Массив;
Для Каждого Строка Из СтрокиДерева Цикл
// Рекурсивно заходим в подчиненные строки
Если Строка.Строки.Количество() > 0 Тогда
ФильтроватьДерево(Строка.Строки, ЗначениеПоиска);
КонецЕсли;
// Проверяем: если после очистки детей у строки нет и сама она не подходит
// под условие — добавляем её в массив на удаление
ПодходитПодУсловие = Найти(НРег(Строка.НоменклатураНаименование), НРег(ЗначениеПоиска)) > 0;
Если НЕ ПодходитПодУсловие И Строка.Строки.Количество() = 0 Тогда
МассивНаУдаление.Добавить(Строка);
КонецЕсли;
КонецЦикла;
Для Каждого СтрокаДляУдаления Из МассивНаУдаление Цикл
СтрокиДерева.Удалить(СтрокаДляУдаления);
КонецЦикла;
КонецПроцедуры
Этот метод гарантирует, что если хотя бы один «внук» соответствует фильтру, вся цепочка его предков останется в дереве. При необходимости реализовать нечеткий интеллектуальный поиск с учетом опечаток можно применить алгоритм сравнения строк Левенштейна.
Объект КоллекцияСтрокДереваЗначений обладает встроенным методом НайтиСтроки. Он работает достаточно быстро, но имеет важную особенность: он возвращает массив ссылок на строки, а не новое дерево. Если мы просто очистим исходное дерево, ссылки в массиве станут недействительными.
Рассмотрим правильный подход с использованием копирования:
Скопировать().НайтиСтроки для поиска нужных элементов в оригинале.Гораздо эффективнее использовать НайтиСтроки для идентификации объектов, которые НУЖНО удалить, как это предлагалось в обсуждении на форуме:
// Пример отбора "Пустых" позиций
Отбор = Новый Структура("Количество", 0);
МассивСтрок = ДеревоДанных.Строки.НайтиСтроки(Отбор, Истина); // Параметр Истина включает поиск по всем уровням
Для Каждого Стр Из МассивСтрок Цикл
Попытка
Стр.Родитель.Строки.Удалить(Стр);
Исключение
ДеревоДанных.Строки.Удалить(Стр); // Если это верхний уровень
КонецПопытки;
КонецЦикла;
Как отметили участники обсуждения, ПостроительЗапроса — это мощный инструмент для работы с данными. Он умеет накладывать стандартные 1С-овские фильтры. Однако напрямую ДеревоЗначений он воспринимает не всегда корректно (часто «схлопывает» до плоской таблицы).
Чтобы этот метод сработал, рекомендуется использовать в качестве источника данных не само дерево, а запрос, который это дерево формирует. В тексте запроса следует использовать конструкцию {ГДЕ ...}. Тогда вы сможете программно передать настройки отбора, и на выходе получить уже отфильтрованный результат запроса методом Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам).
Построитель = Новый ПостроительЗапроса;
Построитель.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Количество КАК Кол
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|{ГДЕ
| Номенклатура.Ссылка.* КАК Номенклатура}";
Построитель.ЗаполнитьНастройки();
// Здесь пользователь или программист задает отбор
НовыйОтбор = Построитель.Отбор.Добавить("Номенклатура");
НовыйОтбор.ВидСравнения = ВидСравнения.ВИерархии;
НовыйОтбор.Использование = Истина;
Построитель.Выполнить();
ДеревоРезультат = Построитель.Результат.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамИИерархии);
Если вы работаете на платформе 8.3 с управляемыми формами, можно обойтись без удаления строк. Это сохранит целостность данных и повысит производительность. Для быстрой генерации правил отображения элементов формы можно использовать конструктор кода условного оформления.
Выясним, как это реализовать:
ДеревоЗначений служебную колонку Видимость (тип Булево).Истина для тех строк, которые должны быть видны.Видимость равно Ложь, то установить параметр Видимость в значение Ложь для всей строки.Преимущество этого метода в том, что пользователь может мгновенно сбросить поиск, и нам не придется заново запрашивать данные из базы или копировать дерево.
Начиная с версии платформы 8.3.15, у элемента формы Таблица (при отображении дерева) появилось свойство Строка поиска. Если его включить, платформа сама отрисует поле ввода. При вводе текста 1С автоматически отфильтрует дерево, показывая только совпадения и их родителей. Это самый быстрый способ, не требующий написания кода, но он ограничен только простым текстовым поиском по видимым колонкам.
Выбор конкретного метода зависит от вашей конфигурации и сложности условий:
Строку поиска.Условное оформление.Построитель или СКД.Рассмотренные подходы позволяют эффективно управлять иерархическими данными, обеспечивая пользователю удобный инструмент для работы с большими списками номенклатуры и комплектующих.