В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с задачей фильтрации данных в табличной части. Стандартный механизм ОтборСтрок (RowFilter) отлично работает, когда нужно найти точное совпадение в одной колонке. Однако, когда возникает задача найти значение (например, фамилию водителя), которое может находиться в любой из четырех разных колонок (Водитель 1, Водитель 2 и т.д.), мы упираемся в ограничение платформы: свойство ОтборСтрок принимает ФиксированнуюСтруктуру, которая объединяет условия только по логическому «И». Нам же требуется логическое «ИЛИ».
В этой статье мы подробно разберем три основных способа решения этой задачи, проанализируем их плюсы и минусы, а также научимся писать код для каждого случая.
Рассмотрим самый популярный и производительный метод для обычных табличных частей. Суть его заключается в создании «индекса» — специальной текстовой колонки, в которой будут собраны строковые представления всех водителей из текущей строки.
Разберем реализацию по шагам:
СоставПлана новый реквизит СтрокаПоиска (тип Строка, длина 500).Видимость в значение Ложь.Проанализируем код, который необходимо разместить в модуле формы для обновления этого поля:
&НаСервере
Процедура ОбновитьСтрокуПоиска(СтрокаТаблицы)
// Собираем наименования всех водителей в одну строку
ТекстПоиска = "";
Если ЗначениеЗаполнено(СтрокаТаблицы.Водитель_1) Тогда
ТекстПоиска = ТекстПоиска + Строка(СтрокаТаблицы.Водитель_1) + " ";
КонецЕсли;
Если ЗначениеЗаполнено(СтрокаТаблицы.Водитель_2) Тогда
ТекстПоиска = ТекстПоиска + Строка(СтрокаТаблицы.Водитель_2) + " ";
КонецЕсли;
// ... повторить для всех необходимых колонок
СтрокаТаблицы.СтрокаПоиска = СокрЛП(ТекстПоиска);
КонецПроцедуры
Теперь мы можем использовать стандартный ОтборСтрок, но искать не по ссылке, а по вхождению текста в нашу вспомогательную колонку. Обратите внимание, что ОтборСтрок в управляемых формах при работе со строками ищет по вхождению (подстроке), что нам и нужно.
Если нам нужно искать именно по ссылке (например, когда в поле поиска выбран конкретный элемент справочника Водители), лучше использовать метод с булевым флагом. Этот способ позволяет избежать проблем с одинаковыми фамилиями в текстовом поиске.
Рассмотрим алгоритм действий:
ПодходитПодФильтр с типом Булево.ПриИзменении поля поиска напишем цикл, который проставит этот флаг для каждой строки.Проанализируем пример кода:
&НаКлиенте
Процедура ПоискПоВодителюПриИзменении(Элемент)
ПараметрПоиска = ПоискПоВодителю; // Ссылка на выбранного водителя
Для Каждого СтрокаТЧ Из Объект.СоставПлана Цикл
// Проверяем наличие водителя в любой из 4 колонок
Если Не ЗначениеЗаполнено(ПараметрПоиска) Тогда
СтрокаТЧ.ПодходитПодФильтр = Истина;
Иначе
Условие = (СтрокаТЧ.Водитель_1 = ПараметрПоиска)
ИЛИ (СтрокаТЧ.Водитель_2 = ПараметрПоиска)
ИЛИ (СтрокаТЧ.Водитель_3 = ПараметрПоиска)
ИЛИ (СтрокаТЧ.Водитель_4 = ПараметрПоиска);
СтрокаТЧ.ПодходитПодФильтр = Условие;
КонецЕсли;
КонецЦикла;
// Накладываем визуальный отбор
Если ЗначениеЗаполнено(ПараметрПоиска) Тогда
Элементы.СоставПлана.ОтборСтрок = Новый ФиксированнаяСтруктура("ПодходитПодФильтр", Истина);
Иначе
Элементы.СоставПлана.ОтборСтрок = Неопределено;
КонецЕсли;
КонецПроцедуры
Преимущество этого метода в том, что он очень гибкий. Вы можете реализовать любую сложную логику проверки в теле цикла, не ограничиваясь простым сравнением.
Как справедливо заметили участники обсуждения на форуме, наиболее правильным архитектурным решением для поиска в больших объемах данных является использование объекта ДинамическийСписок вместо обычной табличной части. Это позволяет перенести нагрузку по фильтрации на сервер базы данных (SQL).
Выясним причину, почему это лучше: динамический список поддерживает полноценный механизм компоновки данных (СКД), где можно программно создавать группы условий «ИЛИ».
Посмотрим на пример программного формирования такого отбора в динамическом списке:
&НаСервере
Процедура УстановитьОтборИлиНаСервере(ЗначениеПоиска)
Список = Элементы.СоставПланаДинСписок; // Предположим, мы используем дин. список
Список.Отбор.Элементы.Очистить();
Если Не ЗначениеЗаполнено(ЗначениеПоиска) Тогда
Возврат;
КонецЕсли;
// Создаем группу "ИЛИ"
ГруппаИЛИ = Список.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИЛИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
// Добавляем условия для каждой колонки
Для Номер = 1 По 4 Цикл
ИмяПоля = "Водитель_" + Номер;
ЭлементОтбора = ГруппаИЛИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(ИмяПоля);
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = ЗначениеПоиска;
ЭлементОтбора.Использование = Истина;
КонецЦикла;
КонецПроцедуры
Этот способ является «золотым стандартом» 1С, так как он обеспечивает максимальную производительность и корректность работы интерфейса при больших объемах данных.
Подводя итоги нашего анализа, дадим несколько практических советов:
Помните, что при использовании программного изменения данных в табличной части (Методы 1 и 2), необходимо следить за тем, чтобы ОтборСтрок обновлялся каждый раз после модификации данных пользователем. Для этого используйте событие ПриИзменении соответствующих колонок водителей.