Ускоряем медленный/долгий и тормозной стандартный поиск по динамическому списку, настраиваем его под себя

19.01.21

Разработка - Механизмы платформы 1С

Пользователи привыкли искать на форме списка, но, вбивая в поиск некорректные данные, могут завесить всю систему, а если еще искать нужно по НЕ полям списка, то решение только в своём поиске - все это решим в публикации с открытым кодом.

Мы столкнулись с подобными задачами при разработке Кронос: WMS (интерфейс Такси, управляемые формы)- искать номенклатуру/МТР/товар по штрих-коду, при условии, что у одной номенклатуры может быть несколько штрих-кодов, а справочник порядка 700 000 позиций. Стандартный поиск не позволял реализовать подобное, его скорость нас не устроила. 

Решений у этой задачки несколько, например, можно сначала найти нужную нам номенклатуру, а потом установить отбор динамического списка по ней или изменить текст запроса списка таким образом, чтобы он фильтровал список по нужным нам значениям. Дополнительно учтём, что пользователь хотел бы видеть на экране первым, что больше подходит под поиск, иными словами, добавим сортировку. Поэтому в итоге выбрали второй путь и сделали универсальный инструмент/механизм, который можно легко тиражировать в другие места конфигурации.

 

Итак, приступим.

Для начала немного изменим наш типовой запрос, добавив в него ПриоритетШаблона и раздел ГДЕ:

 

Добавим в настройках динамического списка порядок:

 

Далее, добавим наше поле Поиск, отключив стандартный поиск:

 

Добавляем процедуру по обработке ввода значения в поле Поиск:

&НаСервере
Процедура ПоискПриИзмененииСервер()
	
	Если ПустаяСтрока(Поиск) Тогда
		Элементы.Список.Отображение = ОтображениеТаблицы.ИерархическийСписок;
	Иначе
		Элементы.Список.Отображение = ОтображениеТаблицы.Список;
	КонецЕсли;
	
	_ДиалогСервер.УправлениеПоискомПоШаблону(Список, Поиск);
КонецПроцедуры	

&НаКлиенте
Процедура ПоискПриИзменении(Элемент)
	ПоискПриИзмененииСервер();
КонецПроцедуры

Как видно, если поле Поиск заполнено, то мы переключаем вид отображения с Иерархического списка на отображение таблиц Список, чтобы пользователю не делать это вручную.

 

Теперь переходим к написанию самой универсальной функции поиска через изменения текста запроса:

Процедура УправлениеПоискомПоШаблону(Список, СловоПоиска) экспорт

 

Получим метаданные основной таблицы списка:

МД = Метаданные.НайтиПоПолномуИмени(Список.ОсновнаяТаблица);

Если у нас поиск стандартный, только по полям, указанным в конфигураторе ВводПоСтроке, то массив

 полей доступных для поиска получаем таким образом:

МассивПолей = СтрРазделить(МД.ВводПоСтроке, ",");

Если у нас что-то более сложное, или хочется какие-то метаданные обработать иначе:

Если МД=Метаданные.РегистрыСведений.ХХХХ Тогда
			
	МассивПолей = Новый Массив();
		
	МассивПолей.Добавить("Ячейка.Код");
	МассивПолей.Добавить("Паллета.Код");
	МассивПолей.Добавить("Родитель");
	МассивПолей.Добавить("НомерПартии");
	МассивПолей.Добавить("Маркировка");

КонецЕсли;	

 

Далее, сформируем условие для раздела ГДЕ поиска по штрих-коду (номенклатуры или документа), например, таким образом: 

ГДЕПоШтрихКоду = "";
		Если НЕ Метаданные.Документы.Найти(МД.Имя)=Неопределено Тогда
			
			МассивПолей.Добавить("Контрагент.Наименование");
			МассивПолей.Добавить("СкладскаяОперация.Наименование");
			
			ЭтоГруппа 		= "  "; 
			ГДЕПоШтрихКоду 	= " ИЛИ Ссылка В(ВЫБРАТЬ ВЫБОР КОГДА Номенклатура=Значение(Справочник.Номенклатура.ПустаяСсылка) ТОГДА Объект ИНАЧЕ Номенклатура КОНЕЦ ИЗ РегистрСведений.лгШтрихКоды КАК РС ГДЕ РС.ШтрихКод=&СловоПоиска) ";
			ТекстПометкаУдаления 	= " И ПометкаУдаления=Ложь ";
									
		ИначеЕсли НЕ Метаданные.Справочники.Найти(МД.Имя)=Неопределено Тогда
			
			ЭтоГруппа 		= " И (&ПоискПоШаблону=ЛОЖЬ ИЛИ (&ПоискПоШаблону=ИСТИНА  И ЭтоГруппа=ЛОЖЬ)) ";
			ГДЕПоШтрихКоду 	= " ИЛИ Ссылка В(ВЫБРАТЬ ВЫБОР КОГДА Номенклатура=Значение(Справочник.Номенклатура.ПустаяСсылка) ТОГДА Объект ИНАЧЕ Номенклатура КОНЕЦ ИЗ РегистрСведений.лгШтрихКоды КАК РС ГДЕ РС.ШтрихКод=&СловоПоиска) ";
			ТекстПометкаУдаления = " И ПометкаУдаления=Ложь ";
			
		КонецЕсли;	

 

В конце сформируем полное условие ГДЕ и Приоритет для отображения пользователю:

		Добавка = ""; ПриоритетШаблонаПоСлову = ""; ПриоритетШаблонаПоПодобию = "";		
		Для Каждого Поле Из МассивПолей Цикл
			
			Если НЕ ЗначениеЗаполнено(Поле) Тогда
				Продолжить;
			КонецЕсли;	
			
			ПриоритетШаблонаПоСлову 	= ПриоритетШаблонаПоСлову + Добавка+ Поле + "=&СловоПоиска ";
			ПриоритетШаблонаПоПодобию 	= ПриоритетШаблонаПоПодобию + Добавка + Поле + " ПОДОБНО &ШаблонПоиска  СПЕЦСИМВОЛ ""~""  ";
			
			Добавка = " ИЛИ ";
		КонецЦикла;	

		Если ПустаяСтрока(ПриоритетШаблонаПоСлову) Тогда
			Возврат;
		КонецЕсли;
		
		ПриоритетШаблона = 	"ВЫБОР 
		|	КОГДА   &ПоискПоШаблону=ИСТИНА И (" + ПриоритетШаблонаПоСлову +")"+ ТекстПометкаУдаления+" ТОГДА 10000
		|	КОГДА   &ПоискПоШаблону=ИСТИНА И (" + ПриоритетШаблонаПоПодобию +")"+ ТекстПометкаУдаления+" ТОГДА 1000
		|ИНАЧЕ 0 КОНЕЦ	КАК ПриоритетШаблона";

		
		ТекстГДЕПоискПоШаблону = " И (&ПоискПоШаблону=ЛОЖЬ 
		|	ИЛИ " + ПриоритетШаблонаПоПодобию + ГДЕПоШтрихКоду+")" + ЭтоГруппа + ГДЕПериод;
		
						
		Список.ТекстЗапроса = СтрЗаменить(Список.ТекстЗапроса, "0 КАК ПриоритетШаблона", ПриоритетШаблона);
		
		Список.ТекстЗапроса = СтрЗаменить(Список.ТекстЗапроса, "//ТекстГДЕПоискПоШаблону", ТекстГДЕПоискПоШаблону);
		

 

В итоге мы получим текст запроса, который фильтрует на сервере данные по нужным нам условиям, с учётом ограничений нашей конфигурации или ее особенностей, а также заполнено поле для сортировки результатов.

 

Установим параметры нашего нового запроса:

ШаблонПоиска = СокрЛП(СловоПоиска);
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "~", "~~");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "%", "~%");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "_", "~_");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "[", "~[");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "-", "~-");
	ШаблонПоиска = "%" + ШаблонПоиска + "%";
	
	Если НЕ ПустаяСтрока(ГДЕПериод) Тогда
		Список.Параметры.УстановитьЗначениеПараметра("ДатаНачала", 	ДатаНачала);
	КонецЕсли;
	
	Список.Параметры.УстановитьЗначениеПараметра("ШаблонПоиска", 	ШаблонПоиска);
	Список.Параметры.УстановитьЗначениеПараметра("СловоПоиска", 	СловоПоиска);
	Список.Параметры.УстановитьЗначениеПараметра("ПоискПоШаблону", 	НЕ ПустаяСтрока(СловоПоиска));
		

 

А также отключим пользовательскую сортировку, если такая им была включена, вернув к сортировке по умолчанию (заводским настройкам):

Если НЕ ПустаяСтрока(СловоПоиска) Тогда //отключим пользовательскую сортировку
				
		Для Каждого Элемент Из Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
			
			Если ТипЗнч(Элемент)=Тип("ПорядокКомпоновкиДанных") Тогда
				
				Элемент.Элементы.Очистить();
						
				Для Каждого Порядок Из Список.КомпоновщикНастроек.Настройки.Порядок.Элементы Цикл
							
					НовыйПорядок = Элемент.Элементы.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
					
					ЗаполнитьЗначенияСвойств(НовыйПорядок, Порядок);
					
				КонецЦикла;
				
			КонецЕсли;	
			
		КонецЦикла;	
			
	КонецЕсли;	

 

Итак, вот какая функция получается в итоге наших действий:


Процедура УправлениеПоискомПоШаблону(Список, СловоПоиска) экспорт
	
	
	Если ПустаяСтрока(Список.ОсновнаяТаблица) Тогда
		Возврат;
	КонецЕсли;
	
	Если Найти(Список.ТекстЗапроса, "//ТекстГДЕПоискПоШаблону")>0 Тогда
		
		МД = Метаданные.НайтиПоПолномуИмени(Список.ОсновнаяТаблица);
		
		ГДЕПериод = "";
		
		Если МД=Метаданные.РегистрыСведений.ХХХХ Тогда
			
			МассивПолей = Новый Массив();
			
			МассивПолей.Добавить("Ячейка.Код");
			МассивПолей.Добавить("Паллета.Код");
			МассивПолей.Добавить("Родитель");
			МассивПолей.Добавить("НомерПартии");
			МассивПолей.Добавить("Маркировка");
			МассивПолей.Добавить("Спецификация");
			
		Иначе	
			МассивПолей = СтрРазделить(МД.ВводПоСтроке, ",");
		КонецЕсли;	
		
		
		ГДЕПоШтрихКоду = "";
		Если НЕ Метаданные.Документы.Найти(МД.Имя)=Неопределено Тогда
			
			МассивПолей.Добавить("Контрагент.Наименование");
			МассивПолей.Добавить("СкладскаяОперация.Наименование");
			
			ЭтоГруппа 		= "  "; 
			ГДЕПоШтрихКоду 	= " ИЛИ Ссылка В(ВЫБРАТЬ ВЫБОР КОГДА Номенклатура=Значение(Справочник.Номенклатура.ПустаяСсылка) ТОГДА Объект ИНАЧЕ Номенклатура КОНЕЦ ИЗ РегистрСведений.лгШтрихКоды КАК РС ГДЕ РС.ШтрихКод=&СловоПоиска) ";
			ТекстПометкаУдаления 	= " И ПометкаУдаления=Ложь ";
									
		ИначеЕсли НЕ Метаданные.Справочники.Найти(МД.Имя)=Неопределено Тогда
			
			ЭтоГруппа 		= " И (&ПоискПоШаблону=ЛОЖЬ ИЛИ (&ПоискПоШаблону=ИСТИНА  И ЭтоГруппа=ЛОЖЬ)) ";
			ГДЕПоШтрихКоду 	= " ИЛИ Ссылка В(ВЫБРАТЬ ВЫБОР КОГДА Номенклатура=Значение(Справочник.Номенклатура.ПустаяСсылка) ТОГДА Объект ИНАЧЕ Номенклатура КОНЕЦ ИЗ РегистрСведений.лгШтрихКоды КАК РС ГДЕ РС.ШтрихКод=&СловоПоиска) ";
			ТекстПометкаУдаления = " И ПометкаУдаления=Ложь ";
			
		КонецЕсли;	
		
		Добавка = ""; ПриоритетШаблонаПоСлову = ""; ПриоритетШаблонаПоПодобию = "";		
		Для Каждого Поле Из МассивПолей Цикл
			
			Если НЕ ЗначениеЗаполнено(Поле) Тогда
				Продолжить;
			КонецЕсли;	
			
			ПриоритетШаблонаПоСлову 	= ПриоритетШаблонаПоСлову + Добавка+ Поле + "=&СловоПоиска ";
			ПриоритетШаблонаПоПодобию 	= ПриоритетШаблонаПоПодобию + Добавка + Поле + " ПОДОБНО &ШаблонПоиска  СПЕЦСИМВОЛ ""~""  ";
			
			Добавка = " ИЛИ ";
		КонецЦикла;
		
			
		Если ПустаяСтрока(ПриоритетШаблонаПоСлову) Тогда
			Возврат;
		КонецЕсли;
		
		ПриоритетШаблона = 	"ВЫБОР 
		|	КОГДА   &ПоискПоШаблону=ИСТИНА И (" + ПриоритетШаблонаПоСлову +")"+ ТекстПометкаУдаления+" ТОГДА 10000
		|	КОГДА   &ПоискПоШаблону=ИСТИНА И (" + ПриоритетШаблонаПоПодобию +")"+ ТекстПометкаУдаления+" ТОГДА 1000
		|ИНАЧЕ 0 КОНЕЦ	КАК ПриоритетШаблона";

		
		ТекстГДЕПоискПоШаблону = " И (&ПоискПоШаблону=ЛОЖЬ 
		|	ИЛИ " + ПриоритетШаблонаПоПодобию + ГДЕПоШтрихКоду+")" + ЭтоГруппа + ГДЕПериод;
		
						
		Список.ТекстЗапроса = СтрЗаменить(Список.ТекстЗапроса, "0 КАК ПриоритетШаблона", ПриоритетШаблона);
		
		Список.ТекстЗапроса = СтрЗаменить(Список.ТекстЗапроса, "//ТекстГДЕПоискПоШаблону", ТекстГДЕПоискПоШаблону);
		
	КонецЕсли;	
	
	ШаблонПоиска = СокрЛП(СловоПоиска);
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "~", "~~");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "%", "~%");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "_", "~_");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "[", "~[");
	ШаблонПоиска = СтрЗаменить(ШаблонПоиска, "-", "~-");
	ШаблонПоиска = "%" + ШаблонПоиска + "%";
	
	Если НЕ ПустаяСтрока(ГДЕПериод) Тогда
		Список.Параметры.УстановитьЗначениеПараметра("ДатаНачала", 	ДатаНачала);
	КонецЕсли;
	
	Список.Параметры.УстановитьЗначениеПараметра("ШаблонПоиска", 	ШаблонПоиска);
	Список.Параметры.УстановитьЗначениеПараметра("СловоПоиска", 	СловоПоиска);
	Список.Параметры.УстановитьЗначениеПараметра("ПоискПоШаблону", 	НЕ ПустаяСтрока(СловоПоиска));
		
	Если НЕ ПустаяСтрока(СловоПоиска) Тогда //отключим пользовательскую сортировку
				
		Для Каждого Элемент Из Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
			
			Если ТипЗнч(Элемент)=Тип("ПорядокКомпоновкиДанных") Тогда
				
				Элемент.Элементы.Очистить();
						
				Для Каждого Порядок Из Список.КомпоновщикНастроек.Настройки.Порядок.Элементы Цикл
							
					НовыйПорядок = Элемент.Элементы.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
					
					ЗаполнитьЗначенияСвойств(НовыйПорядок, Порядок);
					
				КонецЦикла;
				
			КонецЕсли;	
			
		КонецЦикла;	
			
	КонецЕсли;	
	
КонецПроцедуры	

 

Таким образом, мы решили с Вами поставленную задачу - поиск работает быстро, сортирует для пользователя, ищем только по выбранным полям, а также по точному или неточному (по части строки) совпадению и главное его быстро и легко встроить в любой динамический список конфигурации. 

 

Надеюсь, моя публикация была Вам полезна и сэкономит Ваше время, ссылка на все публикации SizovE, например, в публикации

Перенос отбора/периода при интерактивной смене варианта отчета/СКД в БСП 3.1 (1C ERP, УТ, БП, ...) мной было рассмотрено полезное улучшение стандартного механизма библиотеки стандартных подсистем. 

Подписывайтесь на мой канал (наверху), будет много интересного бесплатного контента :)

См. также

Сервисы интеграции без Шины и интеграции

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пример использования «Сервисов интеграции» без подключения к Шине и без обменов.

13.03.2024    2648    dsdred    16    

59

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Обмен между базами 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    6118    dsdred    59    

86

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    6021    YA_418728146    25    

68

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    7113    dsdred    36    

113

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    19214    SeiOkami    46    

119

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12932    human_new    27    

76

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    9540    YA_418728146    6    

143

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6575    sebekerga    54    

95
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. zhuravlev_as 395 22.12.20 10:38 Сейчас в теме
Кажется, что содержимое данной публикации разработчики не учли в платформе.
2. SizovE 263 22.12.20 10:55 Сейчас в теме
(1) Согласен. Однако, к сожалению, этим страдают многие универсальные вещи - либо простота (галочкой включил/выключил), либо сложная настройка под себя. Думаю 1С пока решила, что хватает галочки, может когда-нибудь сделают более гибкую настройку, надо же что-то делать в следующих релизах )
Оставьте свое сообщение