При разработке интерфейсов на платформе 1С программисты часто сталкиваются с задачей фильтрации данных в динамических списках. Ситуация существенно усложняется, когда поиск необходимо выполнить по текстовым полям с «нестабильным» форматом данных. Типичный пример — чертежные номера или артикулы (например, 254.587.57, 254. 587. 57, 254.587.57. Шайба), которые пользователи могут вводить с произвольным количеством пробелов, точек и спецсимволов.
Если выполнять обычный поиск силами пользователя на форме легко, то автоматизировать этот процесс при открытии формы — задача не всегда тривиальная. В этой статье мы подробно разберем несколько профессиональных подходов к реализации программного поиска и фильтрации в динамическом списке, оценим их производительность и выберем оптимальные решения.
Самый простой и очевидный способ, к которому часто прибегают разработчики — это программное добавление элемента отбора в настройки компоновки данных динамического списка. В данном случае мы используем вид сравнения Содержит. При необходимости можно использовать материал с глубоким разбором системы настроек компоновки данных динамического списка.
Рассмотрим пример реализации этого метода на практике:
&НаСервере
Процедура УстановитьОтборПоНаименованиюНаСервере()
// Очищаем существующие отборы по данному полю, чтобы избежать зацикливания и дублирования
ПолеОтбора = Новый ПолеКомпоновкиДанных("НаименованиеЧертежа");
Для Каждого Элемент Из Список.Отбор.Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") И Элемент.ЛевоеЗначение = ПолеОтбора Тогда
Список.Отбор.Элементы.Удалить(Элемент);
КонецЕсли;
КонецЦикла;
// Добавляем новый элемент отбора
НовыйЭлемент = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйЭлемент.ЛевоеЗначение = ПолеОтбора;
НовыйЭлемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Содержит;
НовыйЭлемент.Использование = Истина;
// Делаем отбор скрытым от пользователя, чтобы он не мог случайно его сломать
НовыйЭлемент.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
НовыйЭлемент.ПравоеЗначение = Объект.ЧертежныйНомер;
КонецПроцедуры
Преимущества метода: простота реализации, не требует предварительной подготовки данных или индексации базы данных.
Недостатки метода: критически низкая скорость работы на больших базах данных. При использовании условия Содержит платформа транслирует запрос к СУБД с оператором LIKE '%СтрокаПоиска%'. СУБД в этом случае не может использовать стандартные индексы, что приводит к полному сканированию таблицы (Full Table Scan). Даже на относительно небольших таблицах (от нескольких десятков тысяч записей) этот метод вызовет заметные задержки интерфейса.
Начиная с версии платформы 1С 8.3.10 у разработчиков появилась возможность программно управлять стандартным полем поиска динамического списка. Свойство СтрокаПоиска (или SearchString в англоязычной нотации) позволяет симулировать ручной ввод текста пользователем в строку поиска формы.
Разберем, как применить это свойство при открытии формы:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Передаем искомую строку напрямую в элемент управления списком
Элементы.Список.СтрокаПоиска = Объект.ЧертежныйНомер;
КонецПроцедуры
Для того чтобы данный механизм работал корректно и быстро, необходимо соблюдать важные условия:
Авто или Использовать.При использовании этого свойства платформа самостоятельно анализирует доступные проиндексированные поля и мгновенно выводит результат без генерации тяжелых SQL-запросов с оператором LIKE.
Если стандартного поведения строки поиска недостаточно (например, требуется тонкая предварительная обработка поискового запроса или поиск по сложным правилам), мы можем объединить возможности глобального полнотекстового поиска системы 1С и динамического списка. Для таких случаев также подойдёт инструмент для семантического поиска по справочникам 1С.
Идея метода заключается в следующем: мы программно выполняем быстрый полнотекстовый поиск по нужному регистру или справочнику, получаем массив подходящих ключей записей или ссылок, а затем накладываем на динамический список легкий отбор с видом сравнения В списке.
Реализуем данный алгоритм по шагам:
&НаСервере
Процедура ВыполнитьБыстрыйПоискНаСервере(СтрокаПоиска)
// Очищаем предыдущие отборы по ключам записей
ПолеКлючЗаписи = Новый ПолеКомпоновкиДанных("КлючЗаписи");
Для Каждого Элемент Из Список.Отбор.Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") И Элемент.ЛевоеЗначение = ПолеКлючЗаписи Тогда
Список.Отбор.Элементы.Удалить(Элемент);
КонецЕсли;
КонецЦикла;
Если ПустаяСтрока(СтрокаПоиска) Тогда
Возврат;
КонецЕсли;
РазмерПорции = 100; // Ограничим выборку разумным количеством релевантных записей
СписокПоиска = ПолнотекстовыйПоиск.СоздатьСписок(СтрокаПоиска, РазмерПорции);
СписокПоиска.ОбластьПоиска.Добавить(Метаданные.РегистрыСведений.БазаЧертежей);
// Выполняем поиск по индексу
СписокПоиска.ПерваяЧасть();
Если СписокПоиска.ПолноеКоличество() = 0 Тогда
// Если ничего не найдено, устанавливаем заведомо невыполнимый отбор
НовыйЭлемент = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйЭлемент.ЛевоеЗначение = ПолеКлючЗаписи;
НовыйЭлемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
НовыйЭлемент.ПравоеЗначение = Неопределено;
Возврат;
КонецЕсли;
// Собираем ключи найденных записей регистра
МассивКлючей = Новый Массив;
Для Каждого Результат Из СписокПоиска Цикл
// Для регистра сведений значением будет КлючЗаписи, для справочника - Ссылка
МассивКлючей.Добавить(Результат.Значение);
КонецЦикла;
// Устанавливаем быстрый отбор по индексу СУБД
НовыйЭлемент = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйЭлемент.ЛевоеЗначение = ПолеКлючЗаписи;
НовыйЭлемент.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
НовыйЭлемент.ПравоеЗначение = МассивКлючей;
НовыйЭлемент.Использование = Истина;
КонецПроцедуры
Этот метод работает чрезвычайно быстро, так как полнотекстовый индекс 1С оптимизирован под подобные задачи, а результирующий SQL-запрос от динамического списка выполняет выборку по конкретным ключевым полям, что занимает доли миллисекунд.
Если номера деталей содержат точки, дефисы и пробелы в произвольных местах (например, 12.34.56 и 12 34 56), стандартный полнотекстовый поиск может не найти совпадения из-за разницы в спецсимволах. В таких случаях лучшей практикой является нормализация данных.
Проанализируем шаги по реализации этого подхода:
НаименованиеДляПоиска (тип Строка).Пример функции очистки строки:
Функция ОчиститьСтрокуДляПоиска(ИсходнаяСтрока) Экспорт
// Оставляем только буквы и цифры
ДопустимыеСимволы = "0123456789АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯabcdefghijklmnopqrstuvwxyz";
Результат = "";
Для СимволИндекс = 1 По СтрДлина(ИсходнаяСтрока) Цикл
ТекущийСимвол = Сред(ИсходнаяСтрока, СимволИндекс, 1);
Если Найти(ДопустимыеСимволы, НРег(ТекущийСимвол)) > 0 Тогда
Результат = Результат + ТекущийСимвол;
КонецЕсли;
КонецЦикла;
Возврат НРег(Результат);
КонецФункции
При поиске мы точно так же очищаем поисковый запрос пользователя через эту функцию и выполняем сравнение «Равно» или «Начинается с» по служебному полю НаименованиеДляПоиска. Такой индекс будет работать максимально эффективно.
Если вы выбрали пути оптимизации с использованием полнотекстового поиска (Способ 2 или Способ 3), помните о поддержании актуальности поискового индекса. В самописных конфигурациях без Библиотеки Стандартных Подсистем (БСП) необходимо настроить регламентное задание, которое будет регулярно выполнять обновление индекса:
Процедура РегламентноеОбновлениеИндексаППД() Экспорт
// Обновляем индекс немонопольно (False - частичное обновление, True - разрешить слияние)
ПолнотекстовыйПоиск.ОбновитьИндекс(Ложь, Истина);
КонецПроцедуры
Рекомендуется настроить запуск этого задания с интервалом раз в несколько минут (для динамично меняющихся баз данных) или раз в день (для условно-статичных каталогов файлов и чертежей). При необходимости автоматизировать и другие регламентные задачи, включая регулярное архивирование и тестирование данных по расписанию, применяется программа для технического обслуживания 1С.