В данной статье рассматриваются три способа поиска по дереву значений, отображённому на форме (ДанныеФормыДерево):
- обход дерева рекурсией;
- методом НайтиСтроки();
- наложение отборов через схему компоновки данных (СКД).
Так же в статья рассматривается реализация возможности перехода к строке дерева при выборе конкретного результата поиска двумя способами:
- при поиске рекурсией записывать идентификатор строки в таблицу результата поиска;
- для способа 2 и 3 строить таблицу значений с данными из дерева с указанием идентификатора строки.
Для примера было создано дерево значений (далее ДЗ) со следующими колонками:
|
|
Заполнение дерева во всех трёх способах осуществляется следующими функциями в модуле формы.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ЗаполнитьДерево();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьДерево()
МФИО = СтрРазделить("Абр Мария Сергеевна,Аве Александр Александрович,Авер Елена Валериевна,Тушенко Владимир Петрович,Ага Андрей Эдуардович,Геев Андрей Афанасьевич,Аге Дарья Викторовна,Аки Виктор Николаевич,Але Алексей Андреевич,Але Алексей Владимирович,Кема Зарина Карушовна,Реев Илья Ильич,Андр Александр Степанович,Риашина Ирина Михайловна,Рющенко Евгения Васильевна,Ющенко Кирилл Александрович,Канова Наталья Борисовна,Тонов Владимир Иванович,Тропов Евгений Алексеевич,Опова Яна Олеговна,Ина Светлана Васильевна,Анша Анатолий Алексеевич,Арда Вероника Евгеньевна,Аржа Евгений Сергеевич,Арте Надежда Юрьевна,Аста Ольга Александровна,Атё Николай Владимирович,Ахре Виктор Александрович,Бин Владимир Иванович,Байд Анастасия Николаевна,Баку Евгений Иванович,Бала Галина Ивановна,Бухин Валерий Валерьевич,Бан Константин Александрович,Кина Евгения Алексеевна,Бар Николай Анатольевич",",",Ложь);
МСостояний = Стрразделить("Работает,В отпуске,Уволился",",",ЛОЖЬ);
ГСЧ = Новый ГенераторСлучайныхЧисел(0);
ДеревоЗнч = РеквизитФормыВЗначение("Дерево", Тип("ДеревоЗначений"));
Для каждого элем из МФИО Цикл
НС = ДеревоЗнч.Строки.Добавить();
НС.ФИО = элем;
НС.Состояние = МСостояний.Получить(ГСЧ.СлучайноеЧисло(0,2));
НС.ФОТ = ГСЧ.СлучайноеЧисло(9999,250000);
НС.Выплатили = ГСЧ.СлучайноеЧисло(10000,НС.ФОТ);
ЗаполнитьДеревоСтроки(НС, ГСЧ, ГСЧ.СлучайноеЧисло(1, 4));
КонецЦикла;
ЗначениеВРеквизитФормы(ДеревоЗнч,"Дерево");
КонецПроцедуры // ЗаполнитьДерево()
&НаСервере
Процедура ЗаполнитьДеревоСтроки(РодСтрока, ГСЧ, КоличествоСтрок)
Если КоличествоСтрок = 0 Тогда
Возврат;
КонецЕсли;
Для Ай = 1 по КоличествоСтрок Цикл
НС = РодСтрока.Строки.Добавить();
НС.ФИО = РодСтрока.ФИО;
НС.Состояние = РодСтрока.Состояние;
НС.ФОТ = ГСЧ.СлучайноеЧисло(9999,250000);
НС.Выплатили = ГСЧ.СлучайноеЧисло(10000,НС.ФОТ);
Если ГСЧ.СлучайноеЧисло(0,1) Тогда
ЗаполнитьДеревоСтроки(НС, ГСЧ, ГСЧ.СлучайноеЧисло(0, 2));
КонецЕсли;
КонецЦикла;
КонецПроцедуры // ЗаполнитьДеревоСтроки()
Рассмотри каждый способ отдельно.
Способ первый - Обход дерева рекурсией.
Для реализации данного способа необходимо создать таблицу значений (далее ТЗ), в которую будет выводится результат поиска. Таблица должна иметь такие же колонки как и ДЗ и колонку хранящую индекс в дереве.
Структура таблицы РезультатПоиска:
|
|
Созданное дерево и таблицу вынесем на форму и перейдём к созданию команд поиска.
Имена команд должны соответствовать формату ИмяКолонкиПоиска_ВидСравнения
Созданным командам назначим одинаковое действие ПоискРекурсией:
&НаКлиенте
Процедура ПоискРекурсией(Команда)
КонецПроцедуры
Далее в контекстном меню создадим подменю и добавим команды:
Так как поиск по дереву будет осуществляться из контекстного меню дерева, то для наглядности команды добавим следующий код в обработку события ПриАктивизацииСтроки для дерева:
&НаКлиенте
Процедура ДеревоПриАктивизацииСтроки(Элемент)
ДанныеСтроки = Элементы.Дерево.ТекущиеДанные;
//Колонка ФИО
Элементы.ФИО_Равно.Заголовок = "ФИО = " + ДанныеСтроки.ФИО;
Элементы.ФИО_НеРавно.Заголовок = "ФИО <> " + ДанныеСтроки.ФИО;
//Колонка Состояние
Элементы.Состояние_Равно.Заголовок = "Состояние = " + ДанныеСтроки.Состояние;
Элементы.Состояние_НеРавно.Заголовок = "Состояние <> " + ДанныеСтроки.Состояние;
//Колонка ФОТ
Элементы.ФОТ_Равно.Заголовок = "ФОТ = " + ДанныеСтроки.ФОТ;
Элементы.ФОТ_НеРавно.Заголовок = "ФОТ <> " + ДанныеСтроки.ФОТ;
Элементы.ФОТ_МеньшеИЛИРавно.Заголовок = "ФОТ <= " + ДанныеСтроки.ФОТ;
Элементы.ФОТ_Меньше.Заголовок = "ФОТ < " + ДанныеСтроки.ФОТ;
Элементы.ФОТ_БольшеИлиРавно.Заголовок = "ФОТ >= " + ДанныеСтроки.ФОТ;
Элементы.ФОТ_Больше.Заголовок = "ФОТ > " + ДанныеСтроки.ФОТ;
//Колонка Выплатили
Элементы.Выплатили_Равно.Заголовок = "Выплатили = " + ДанныеСтроки.Выплатили;
Элементы.Выплатили_НеРавно.Заголовок = "Выплатили <> " + ДанныеСтроки.Выплатили;
Элементы.Выплатили_МеньшеИЛИРавно.Заголовок = "Выплатили <= " + ДанныеСтроки.Выплатили;
Элементы.Выплатили_Меньше.Заголовок = "Выплатили < " + ДанныеСтроки.Выплатили;
Элементы.Выплатили_БольшеИлиРавно.Заголовок = "Выплатили >= " + ДанныеСтроки.Выплатили;
Элементы.Выплатили_Больше.Заголовок = "Выплатили > " + ДанныеСтроки.Выплатили;
КонецПроцедуры
Заполним тело процедуры ПоискРекурсией следующим кодом:
&НаКлиенте
Процедура ПоискРекурсией(Команда)
Соответствие = Новый Соответствие;
Соответствие.Вставить("Равно", "=");
Соответствие.Вставить("Больше", ">");
Соответствие.Вставить("БольшеИлиРавно", ">=");
Соответствие.Вставить("Меньше", "<");
Соответствие.Вставить("МеньшеИЛИРавно", "<=");
Соответствие.Вставить("НеРавно", "<>");
ИмяКомандыПоСтрокам = СтрРазделить(Команда.Имя, "_", Ложь);
ИмяКолонки = ИмяКомандыПоСтрокам.Получить(0);
ВидСравненияПоиска = Соответствие.Получить(ИмяКомандыПоСтрокам.Получить(1));
ЗнчПоиска = Элементы.Дерево.ТекущиеДанные[ИмяКолонки];
РезультатПоиска.Очистить();
ПоискПоДеревуРекурсией(Дерево.ПолучитьЭлементы(), ИмяКолонки, ЗнчПоиска, ВидСравненияПоиска);
КонецПроцедуры
&НаКлиенте
Процедура ПоискПоДеревуРекурсией(ЭлементыДерева, ЛевоеЗнч, ПравоеЗнч, ВидСравнения = "=")
Если ВидСравнения = "=" Тогда
Для каждого элем Из ЭлементыДерева Цикл
Если элем[ЛевоеЗнч] = ПравоеЗнч Тогда
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
НС.ИндексВДереве = элем.ПолучитьИдентификатор();
КонецЕсли;
ПоискПоДеревуРекурсией(Элем.ПолучитьЭлементы(),ЛевоеЗнч, ПравоеЗнч, ВидСравнения);
КонецЦикла;
ИначеЕсли ВидСравнения = "<=" Тогда
Для каждого элем Из ЭлементыДерева Цикл
Если элем[ЛевоеЗнч] <= ПравоеЗнч Тогда
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
НС.ИндексВДереве = элем.ПолучитьИдентификатор();
КонецЕсли;
ПоискПоДеревуРекурсией(Элем.ПолучитьЭлементы(),ЛевоеЗнч, ПравоеЗнч, ВидСравнения);
КонецЦикла;
ИначеЕсли ВидСравнения = ">=" Тогда
Для каждого элем Из ЭлементыДерева Цикл
Если элем[ЛевоеЗнч] >= ПравоеЗнч Тогда
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
НС.ИндексВДереве = элем.ПолучитьИдентификатор();
КонецЕсли;
ПоискПоДеревуРекурсией(Элем.ПолучитьЭлементы(),ЛевоеЗнч, ПравоеЗнч, ВидСравнения);
КонецЦикла;
ИначеЕсли ВидСравнения = "<>" Тогда
Для каждого элем Из ЭлементыДерева Цикл
Если элем[ЛевоеЗнч] <> ПравоеЗнч Тогда
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
НС.ИндексВДереве = элем.ПолучитьИдентификатор();
КонецЕсли;
ПоискПоДеревуРекурсией(Элем.ПолучитьЭлементы(),ЛевоеЗнч, ПравоеЗнч, ВидСравнения);
КонецЦикла;
ИначеЕсли ВидСравнения = "<" Тогда
Для каждого элем Из ЭлементыДерева Цикл
Если элем[ЛевоеЗнч] < ПравоеЗнч Тогда
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
НС.ИндексВДереве = элем.ПолучитьИдентификатор();
КонецЕсли;
ПоискПоДеревуРекурсией(Элем.ПолучитьЭлементы(),ЛевоеЗнч, ПравоеЗнч, ВидСравнения);
КонецЦикла;
ИначеЕсли ВидСравнения = ">" Тогда
Для каждого элем Из ЭлементыДерева Цикл
Если элем[ЛевоеЗнч] > ПравоеЗнч Тогда
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
НС.ИндексВДереве = элем.ПолучитьИдентификатор();
КонецЕсли;
ПоискПоДеревуРекурсией(Элем.ПолучитьЭлементы(),ЛевоеЗнч, ПравоеЗнч, ВидСравнения);
КонецЦикла;
КонецЕсли;
КонецПроцедуры // ПоискПоДеревуРекурсией()
Осталось реализовать переход к строке дерева при выборе строки в таблице результата. Для это создадим обработку события Выбор для таблицы результата (ТаблицаРезультат):
И вставим следующий код:
&НаКлиенте
Процедура РезультатПоискаРекурсиейВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Элементы.Дерево.ТекущаяСтрока = Элементы.РезультатПоиска.ТекущиеДанные.ИндексВДереве;
КонецПроцедуры
На этом реализация первого способа закончена.
Способ второй – Метод НайтиСтроки().
Для реализации данного способа нам понадобиться создать таблицу РезультатПоиска, описанную в первом способе и таблицу значений ТаблицаИндексов аналогичную таблице РезультатПоиска.
Данная таблица заполняется процедурой ЗаполнитьТаблицуИндексовРекурсией, которая вызывается в событии ПриСозданииНаСервере после процедуры ЗаполнитьДерево.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ЗаполнитьДерево();
ЗаполнитьТаблицуИндексовРекурсией(Дерево.ПолучитьЭлементы());
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьТаблицуИндексовРекурсией(ЭлементыДерева)
Для каждого элем из ЭлементыДерева Цикл
НС = ТаблицаИндексов.Добавить();
ЗаполнитьЗначенияСвойств(НС, элем);
НС.ИндексВДереве = элем.ПолучитьИдентификатор();
ЗаполнитьТаблицуИндексовРекурсией(элем.ПолучитьЭлементы());
КонецЦикла;
КонецПроцедуры // ЗаполнитьТаблицуИндексовРекурсией()
Поиск по дереву будет осуществляться аналогично первому способу - из контекстного меню. Для этого создадим команды, соответствующие формату Выгрузка_ИмяКолонкиПоиска_Равно. В отличие от первого способа тут можно осуществлять только операцию сравнения.
Созданным командам назначим одинаковое действие ПоискВыгрузкой
&НаКлиенте
Процедура ПоискВыгрузкой(Команда)
ИмяКоманды = Команда.Имя;
//Выгрузка_Выплатили_Равно
ИмяКолонки = СтрЗаменить(СтрЗаменить(ИмяКоманды,"Выгрузка_",""),"_Равно","");
ЗнчПоиска = Элементы.Дерево.ТекущиеДанные[ИмяКолонки];
РезультатПоиска.Очистить();
ПоискВыгрузкойНаСервере(ИмяКолонки, ЗнчПоиска);
// Вставить содержимое обработчика.
КонецПроцедуры
Для наглядности команд создадим процедуру обработки события ПриАктивизацииСтроки для ДЗ:
И вставим в неё следующий код:
&НаКлиенте
Процедура ДеревоПриАктивизацииСтроки(Элемент)
ДанныеСтроки = Элементы.Дерево.ТекущиеДанные;
//Колонка ФИО
Элементы.Выгрузка_ФИО_Равно.Заголовок = "(ВЫГРУЗКА) ФИО = " + ДанныеСтроки.ФИО;
//Колонка Состояние
Элементы.Выгрузка_Состояние_Равно.Заголовок = "(ВЫГРУЗКА) Состояние = " + ДанныеСтроки.Состояние;
//Колонка ФОТ
Элементы.Выгрузка_ФОТ_Равно.Заголовок = "(ВЫГРУЗКА) ФОТ = " + ДанныеСтроки.ФОТ;
//Колонка Выплатили
Элементы.Выгрузка_Выплатили_Равно.Заголовок = "(ВЫГРУЗКА) Выплатили = " + ДанныеСтроки.Выплатили;
КонецПроцедуры
У данного способа есть два пути реализации поиска по дереву:
- Искать по дереву значений. Для этого необходимо используя функцию РеквизитФормыВЗначение для преобразования ДанныеФормыДерево в тип Деревозначений и использовать метод НайтиСтроки().
- Искать по построенной таблице индексов, что является более оптимальным способом поиска.
Для реализации первого пути в тело процедуры ПоискВыгрузкойНаСервере необходимо вставить следующий код:
&НаСервере
Процедура ПоискВыгрузкойНаСервере(ИмяКолонки, ЗнчПоиска)
ДеревоОб = РеквизитФормыВЗначение("Дерево", Тип("ДеревоЗначений"));
МНайденныхСтрок = ДеревоОб.Строки.НайтиСтроки(Новый Структура(ИмяКолонки, ЗнчПоиска),Истина);
Для каждого элем из МНайденныхСтрок Цикл
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
КонецЦикла;
КонецПроцедуры // ПоискВыгрузкойНаСервере()
Для реализации второго пути в процедуру необходимо вставить:
&НаСервере
Процедура ПоискВыгрузкойНаСервере(ИмяКолонки, ЗнчПоиска)
МНайденныхСтрок = ТаблицаИндексов.НайтиСтроки(Новый Структура(ИмяКолонки, ЗнчПоиска));
Для каждого элем из МНайденныхСтрок Цикл
НС = РезультатПоиска.Добавить();
ЗаполнитьЗначенияСвойств(НС, Элем);
КонецЦикла;
КонецПроцедуры // ПоискВыгрузкойНаСервере()
Осталось реализовать переход к строке дерева при выборе строки в таблице результата. Для это создадим обработку события Выбор для таблицы результата (ТаблицаРезультат):
В зависимости от выбранного пути в процедуру необходимо вставить следующий код:
Путь 1:
&НаКлиенте
Процедура РезультатПоискаЧерезВыгрузкуВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
МКолонок = СтрРазделить("ФИО,Состояние,ФОТ,Выплатили", ",", Ложь);
ДанныеОтбора = Новый Структура;
Данные = Элементы.РезультатПоиска.ТекущиеДанные;
Для каждого элем из МКолонок Цикл
ДанныеОтбора.Вставить(элем, Данные[элем]);
КонецЦикла;
Элементы.Дерево.ТекущаяСтрока = ТаблицаИндексов.НайтиСтроки(ДанныеОтбора)[0].ИндексВДереве;
КонецПроцедуры
Путь 2:
&НаКлиенте
Процедура РезультатПоискаЧерезВыгрузкуВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Элементы.Дерево.ТекущаяСтрока = Элементы.РезультатПоиска.ТекущиеДанные.ИндексВДереве;
КонецПроцедуры
На этом реализация второго способа закончена.
Способ третий - Наложение отборов через схему компоновки данных (СКД).
Для реализации данного способа необходимо создать таблицы РезультатПоиска и ТаблицаИндексов описанные в предыдущих способах. Дополнительно в форме обработки создадим реквизит КомпоновщикНастроекКомпоновкиДанных с типом КомпоновщикНастроекКомпоновкиДанных и АдресМакетаОтчетаСКД с типом строка (неограниченной длины).
Также в самой обработке необходимо создать макет с именем Макет с типом Схема компоновки данных.
Зайдём в Макет и добавим набор данных – объект. Создадим в наборе поля аналогичные колонкам из таблицы индексов (или из таблицы результат поиска). У поля ИндексВДереве в ограничениях поставим галочку «недоступно как условие». Зададим имя объекта, содержащие данные: ТаблицаДанныхСКД.
Перейдём в настройки СКД и создадим вариант отчета (если он ещё не создан). Добавим новую группировку – детальные записи. В «выбранные поля» добавим все доступные поля, кроме системных.
На этом настройка подготовка макета закончена. Перейдём в форму обработки.
Вынесем на форму реквизит КомпоновщикНастроекКомпоновкиДанных.Настройки.Отбор. При выборе нового типа элемента выберем «Таблица».
В событие ПриСозданииНаСервере помимо процедур ЗаполнитьДерево и ЗаполнитьТаблицуИндексовРекурсией добавим процедуру ИнициализацияОтборовДерева.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ЗаполнитьДерево();
ЗаполнитьТаблицуИндексовРекурсией(Дерево.ПолучитьЭлементы());
ИнициализацияОтборовДерева();
КонецПроцедуры
В процедуру добавим следующий код:
&НаСервере
Процедура ИнициализацияОтборовДерева()
МОбъект = РеквизитФормыВЗначение("Объект");
СКД = МОбъект.ПолучитьМакет("Макет");
АдресМакетаОтчетаСКД = ПоместитьВоВременноеХранилище(СКД, Новый УникальныйИдентификатор);
КомпоновщикНастроекКомпоновкиДанных.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(АдресМакетаОтчетаСКД));
КомпоновщикНастроекКомпоновкиДанных.ЗагрузитьНастройки(СКД.НастройкиПоУмолчанию);
КонецПроцедуры // ИнициализацияОтборовДерева()
Создадим команду НайтиЧерезСКД и добавим в неё следующий код:
&НаКлиенте
Процедура НайтиЧерезСКД(Команда)
НайтиЧерезСКДНаСервере();
КонецПроцедуры
&НаСервере
Процедура НайтиЧерезСКДНаСервере()
РезультатПоиска.Очистить();
СКД = ПолучитьИзВременногоХранилища(АдресМакетаОтчетаСКД);
НаборыДанных = Новый Структура("ТаблицаДанныхСКД", РеквизитФормыВЗначение("ТаблицаИндексов", Тип("ТаблицаЗначений")));
КомпМакета = новый КомпоновщикМакетаКомпоновкиДанных;
макетКомп = КомпМакета.Выполнить(СКД, КомпоновщикНастроекКомпоновкиДанных.Настройки,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпДанных = новый ПроцессорКомпоновкиДанных;
ПроцессорКомпДанных.Инициализировать(макетКомп, НаборыДанных);
ВыводВТЗ = новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТЗРез = РеквизитФормыВЗначение("РезультатПоиска", Тип("ТаблицаЗначений"));
ВыводВТЗ.УстановитьОбъект(ТЗРез);
ВыводВТЗ.Вывести(ПроцессорКомпДанных);
РезультатПоиска.Загрузить(ТЗрез);
КонецПроцедуры
Вынесем созданную команду на форму в командную панель отборов СКД
Осталось реализовать переход к строке дерева при выборе строки в таблице результата. Для это создадим обработку события Выбор для таблицы результата (ТаблицаРезультат):
&НаКлиенте
Процедура РезультатОтбораСКДВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
Элементы.Дерево.ТекущаяСтрока = Элементы.РезультатПоиска.ТекущиеДанные.ИндексВДереве;
КонецПроцедуры
На этом реализация третьего способа и сама статья закончена.
Спасибо за внимание! Надеюсь, эта статья была полезна:)