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