При работе в устаревших или классических конфигурациях на обычных формах (например, УТ 10.3, УПП 1.3 или КА 1.1) разработчики часто сталкиваются с тем, что стандартные механизмы поиска работают не так удобно, как в современных решениях на управляемых формах (Розница 2.0, УТ 11). Одной из наиболее востребованных функций является «умный» поиск: когда система выдает сначала те позиции, которые начинаются с введенного текста, а затем все остальные, где текст встречается в середине или в конце.
В этой статье мы подробно разберем, как модифицировать стандартный поиск в процедуре ОкончаниеВводаТекста или АвтоПодбор, чтобы добиться идеальной сортировки результатов.
Рассмотрим типичную ситуацию. Программист пишет запрос, который ищет контрагента по вхождению строки. Обычно это выглядит так:
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты ГДЕ Наименование ПОДОБНО &Наименование";
Запрос.УстановитьПараметр("Наименование", "%" + Текст + "%");
Проблема такого кода в том, что база данных SQL (или файловая СУБД) возвращает результат либо в порядке создания записей, либо по алфавиту. Если пользователь вводит «Алт», он хочет первым увидеть «Алтай-Сервис», а не «База Алтайская». Но стандартный поиск выдаст их вперемешку. Чтобы решить эту задачу, нам нужно научить запрос определять «вес» или «приоритет» каждой найденной записи.
Самый быстрый и эффективный способ — добавить в запрос условие, которое будет проверять, начинается ли наименование с искомого текста. Мы присвоим таким записям индекс 0, а всем остальным — индекс 1. Затем просто отсортируем результат по этому индексу.
Разберем реализацию этого метода по шагам:
Текст + "%"), другой для поиска везде ("%" + Текст + "%").ВЫБРАТЬ создадим поле с помощью конструкции ВЫБОР.Посмотрим на пример кода:
Процедура КонтрагентОкончаниеВводаТекста(Элемент, Текст, Значение, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь; // Отключаем стандартный поиск
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| ВЫБОР
| КОГДА Контрагенты.Наименование ПОДОБНО &ТекстНачало ТОГДА 0
| ИНАЧЕ 1
| КОНЕЦ КАК Приоритет
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Наименование ПОДОБНО &ТекстЛюбаяЧасть
|
|УПОРЯДОЧИТЬ ПО
| Приоритет,
| Контрагенты.Наименование";
Запрос.УстановитьПараметр("ТекстНачало", Текст + "%");
Запрос.УстановитьПараметр("ТекстЛюбаяЧасть", "%" + Текст + "%");
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Возврат;
КонецЕсли;
Выборка = Результат.Выбрать();
СписокВыборки = Новый СписокЗначений;
Пока Выборка.Следующий() Цикл
СписокВыборки.Добавить(Выборка.Ссылка);
КонецЦикла;
// Выводим список пользователю для выбора
ВыбранныйЭлемент = ВыбратьИзСписка(СписокВыборки, Элемент);
Если ВыбранныйЭлемент <> Неопределено Тогда
Значение = ВыбранныйЭлемент.Значение;
КонецЕсли;
КонецПроцедуры
Проанализируем альтернативный подход, который предлагался в обсуждении — использование оператора ОБЪЕДИНИТЬ ВСЕ. Этот способ также рабочий, но требует внимательности при настройке сортировки. Мы делаем два запроса: первый выбирает точные совпадения по началу строки, второй — все остальные вхождения. Чтобы результаты не дублировались, во втором запросе нужно исключить те записи, которые уже попали в первый.
Рассмотрим структуру такого запроса:
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| 0 КАК Индекс
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Наименование ПОДОБНО &ТекстНачало
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| Контрагенты.Ссылка,
| 1
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Наименование ПОДОБНО &ТекстЛюбаяЧасть
| И НЕ Контрагенты.Наименование ПОДОБНО &ТекстНачало
|
|УПОРЯДОЧИТЬ ПО
| Индекс,
| Ссылка.Наименование";
Важный момент: использование ОБЪЕДИНИТЬ ВСЕ может работать чуть медленнее на огромных справочниках, так как СУБД приходится обрабатывать два подзапроса. Метод с ВЫБОР (CASE в SQL) обычно считается более элегантным и производительным.
Проанализируем ситуацию: событие ОкончаниеВводаТекста срабатывает только тогда, когда пользователь нажал Enter или Tab. Это не совсем то, что мы видим в Рознице 2.0. Чтобы поиск происходил мгновенно, «на лету», рекомендуется использовать событие АвтоПодбор.
Разберем особенности реализации в АвтоПодбор:
СписокВыбора у элемента управления.СтандартнаяОбработка = Ложь.Пример реализации для создания ощущения «современного» интерфейса:
Процедура КонтрагентАвтоПодбор(Элемент, Текст, ТекстАвтоПодбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Если введено меньше 3 символов, не мучаем базу данных
Если СтрДлина(Текст) < 3 Тогда
Возврат;
КонецЕсли;
// Выполняем запрос из Решения №1...
// ... (код запроса) ...
Элемент.СписокВыбора.Очистить();
Пока Выборка.Следующий() Цикл
Элемент.СписокВыбора.Добавить(Выборка.Ссылка);
КонецЦикла;
КонецПроцедуры
Чтобы ваш поиск был не только удобным, но и профессиональным, выясним несколько критических нюансов:
1. Ограничение количества результатов. Если пользователь введет букву «А», запрос может вернуть тысячи контрагентов. Это приведет к зависанию интерфейса. Всегда используйте ВЫБРАТЬ ПЕРВЫЕ 30 (или 50). Этого достаточно для выбора из выпадающего списка.
2. Проблема индексов. Помните, что условие ПОДОБНО "%Текст%" (с процентом в начале) принципиально не использует индексы базы данных. Это означает «Full Scan» — полный перебор таблицы. Если в справочнике 100 000 записей, поиск будет медленным. Для таких случаев в 1С предусмотрен Полнотекстовый поиск (ПП). Рассмотрим возможность использования ПолнотекстовыйПоиск.СоздатьСписок(), если быстродействие станет проблемой.
3. Спецсимволы. Если пользователь введет символы % или _, поиск может отработать непредсказуемо. В идеале, перед передачей текста в запрос, его нужно очищать от служебных символов 1С или использовать ключевое слово СПЕЦСИМВОЛ в запросе.
4. Улучшение визуализации. Чтобы пользователю было легче ориентироваться, в СписокВыбора можно добавлять не просто ссылку, а строку-представление, включающую ИНН или Код. Используйте метод СписокВыбора.Добавить(Ссылка, Представление).
Подводя итог, мы выяснили, что даже в старых конфигурациях 1С можно реализовать качественный и удобный поиск. Использование вычисляемого приоритета в запросе и переход на событие АвтоПодбор сделает работу пользователей значительно комфортнее.