Опубликован: 14.07.2011 | Уровень: специалист | Доступ: платный
Лекция 8:

Поиск, регламентные задания

Аннотация: В этой лекции рассматривается работа с с полнотекстовым поиском по базе данных и с регламентными заданиями

Цель лекции: научиться использовать возможности полнотекстового поиска по базе данных и регламентных заданий

8.1. Полнотекстовый поиск в базе данных

Базы данных 1С:Предприятие обычно содержат огромное количество информации. Логично было бы предположить, что для того, чтобы ориентироваться в этой информации, неплохо было бы иметь механизм поиска. Такой механизм уже присутствует в базе по умолчанию. Он позволяет задавать поисковые запросы на естественно языке, использовать поисковые операторы ( И, ИЛИ, НЕ, РЯДОМ и другие), искать информацию даже в том случае, если нам известна лишь часть искомого текста.

В частности, при создании новых объектов и их реквизитов, свойство Полнотекстовый поиск устанавливается в значение Использовать, рис. 8.1.

Реквизит включен в полнотекстовый поиск

Рис. 8.1. Реквизит включен в полнотекстовый поиск

Если в режиме 1С:Предприятие выполнить команду Операции > Управление полнотекстовым поиском, мы увидим соответствующее окно, рис. 8.2.

Управление полнотекстовым поиском

Рис. 8.2. Управление полнотекстовым поиском

Кнопка Обновить индекс служит для обновления поискового индекса.

Кнопка Очистить индекс позволяет очищать индекс.

Кнопка Настройка вызывает окно, рис. 8.3, которое содержит лишь один параметр - разрешающий или запрещающий использование полнотекстового поиска. По умолчанию поиск разрешен.

Разрешение или запрещение полнотекстового поиска

Рис. 8.3. Разрешение или запрещение полнотекстового поиска

Полнотекстовый поиск в базе данных возможен лишь тогда, когда построен поисковый индекс. В системе существует два индекса - основной и дополнительный. Основной индекс устроен так, чтобы обеспечить как можно более высокую скорость поиска информации, однако свежая информация в него добавляется относительно медленно. Дополнительный индекс постоянно пополняется свежими данными при внесении в базу новый данных или изменении существующих, но поиск в нем ведется медленнее. Установка Разрешить слияние индексов позволяет системе объединять индексы. Это создает дополнительную нагрузку на систему, но, в итоге, данные из дополнительного индекса переносятся в основной индекс, что ускоряет поиск, а дополнительный индекс очищается.

Нужно отметить, что полнотекстовый поиск в базе данных осуществляется в соответствии с правами пользователя, который осуществляет поиск. То есть, если пользователю запрещен доступ к какому-либо объекту, данные этого объекта в поисковую выдачу не попадут.

Ранее мы не пользовались полнотекстовым поиском, соответственно, поискового индекса у нас пока нет (параметр Дата актуальности индекса пуст). Создадим индекс, нажав на кнопку Обновить индекс, установив предварительно флаг Разрешить слияние индексов. В итоге нам будет выдано сообщение о том, что полнотекстовое индексирование завершено (в нашей маленькой базе это происходит почти мгновенно, а вот в реально используемых базах, размеры которых могут быть очень большими, обновление индекса может занять продолжительное время, поэтому лучше не обновлять индекс во время интенсивного использования системы). Окно управления полнотекстовым поиском приняло вид, показанный на рис. 8.4.

Поисковый индекс обновлен

Рис. 8.4. Поисковый индекс обновлен

Механизм полнотекстового поиска в нашей базе присутствует, но нет поискового интерфейса, который позволял бы пользователю производить такой поиск. Создадим его.

В Конфигураторе добавим в систему новый отчет. Назовем его ПоискВБазеДанных, создадим основную форму отчета, разместим на ней поле ввода с подписью Поисковый запрос, кнопку с именем ВыполнитьПоиск и подписью Искать! и надпись с именем СведенияОПоиске и заголовком-подсказкой Введите текст в поле и нажмите кнопку Искать!. В поле ввода мы будем вносить поисковый запрос, по нажатию на кнопку этот запрос будет выполняться, в надпись СведенияОПоиске будем выводить служебную информацию о выполненном поиске.

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

Удалим из нижней командной панели формы отчета кнопку Сформировать. Визуальная часть разработки формы выполнена, рис. 8.5.

Форма поиска

Рис. 8.5. Форма поиска

Теперь приступим к программной части нашего поискового интерфейса.

Ниже приведен полный код модуля формы, реализующий механизмы поиска данных.

//Эту переменную будем использовать в процедурах
//для обращения к объекту 
Перем СпПолнПоиска;

Процедура ПриОткрытии()
	//Инициализируем переменную СпПолнПоиска
	СпПолнПоиска=ПолнотекстовыйПоиск.СоздатьСписок();
	//Задаем размер порции выдачи поисковых записей
	//по умолчанию это 20
	СпПолнПоиска.РазмерПорции=5;
	//Задаем порог нечеткости - этот параметр
	//позволяет находить в базе не только точные слова
	//но и слова, отличающиеся от запроса, задается в процентах
	СпПолнПоиска.ПорогНечеткости=20;
КонецПроцедуры

Процедура ВыполнитьПоискНажатие(Элемент)
	//Записываем поисковый запрос из поля в
	//свойство СтрокаПоиска объекта СпПолнПоиска
	СпПолнПоиска.СтрокаПоиска=ПоисковыйЗапрос;
	//Получаем первую часть поисковой выдачи
	СпПолнПоиска.ПерваяЧасть();
	//Если поиск не дал результата
	Если СпПолнПоиска.ПолноеКоличество()=0 Тогда
		//Сообщаем об этом
		ЭлементыФормы.СведенияОПоиске.Значение="Поиск не дал результатов";
		//Очищаем поле HTML-документа
		ЭлементыФормы.РезультатыПоиска.УстановитьТекст("");

	Иначе
		//Иначе вызываем процедуру вывода результата поиска
		ВывестиРезультат();
	КонецЕсли;
 КонецПроцедуры
	
 Процедура ВывестиРезультат()
	//Формируем значение надписи, сообщающей о реультатах поиска
	//эта надпись принимает такой вид: "Результаты с 1 по 5 из 10"
	ЭлементыФормы.СведенияОПоиске.Значение="Результаты с "+
		Строка(СпПолнПоиска.НачальнаяПозиция()+1)+ " по " +
		Строка(СпПолнПоиска.НачальнаяПозиция()+
		СпПолнПоиска.Количество()) + " из " +
		Строка(СпПолнПоиска.ПолноеКоличество());
		//Записываем результаты поиска в виде HTML-кода 
		РезультатыПоиска = СпПолнПоиска.ПолучитьОтображение(
			ВидОтображенияПолнотекстовогоПоиска.HTMLТекст);
		//Помещаем полученный HTML-код в поле HTML-документа
		ЭлементыФормы.РезультатыПоиска.УстановитьТекст(РезультатыПоиска);
		//Настраиваем доступность кнопок Вперед и Назад в соответствии
		//с количеством найденных элементов и текущей просматриваемой
		//порцией поисковой выдачи
		НастроитьДоступностьКнопок();
  КонецПроцедуры
  
  Процедура НастроитьДоступностьКнопок()
	//Если есть элементы "впереди"
	Если СпПолнПоиска.ПолноеКоличество()-
		СпПолнПоиска.НачальнаяПозиция()>
		СпПолнПоиска.Количество() 
	Тогда
		//Делаем кнопку Вперед доступной
		ЭлементыФормы.Вперед.Доступность=Истина;
	Иначе
		//Блокируем кнопку Вперед
		ЭлементыФормы.Вперед.Доступность=Ложь;
	КонецЕсли;
	//Если есть элементы "позади"	
	Если СпПолнПоиска.НачальнаяПозиция()> 0 
	Тогда
		//Делаем кнопку Назад доступной
		ЭлементыФормы.Назад.Доступность=Истина;
	Иначе
		//Блокируем кнопку Назад
		ЭлементыФормы.Назад.Доступность=Ложь;
	КонецЕсли;
 КонецПроцедуры

 Процедура НазадНажатие(Элемент)
	//При нажатии на кнопку Назад получаем предыдущую
	//порцию поиска
	СпПолнПоиска.ПредыдущаяЧасть();
	//Выводим результат
	ВывестиРезультат();
КонецПроцедуры

Процедура ВпередНажатие(Элемент)
	//При нажатии кнопки Вперед получаем
	//следующую порцию поиска
	СпПолнПоиска.СледующаяЧасть();
	//Выводим результат
	ВывестиРезультат();
КонецПроцедуры

Процедура РезультатыПоискаonclick(Элемент, pEvtObj)
	//Обработчик события onClick для поля HTML-документа
	//Получим элемент, по которому произведен щелчок
	Element=pEvtObj.srcElement;
	//Проверим идентификатор элемента,
	//если это - элемент списка полнотекстовго поиска
	Если Element.id="FullTextSearchListItem" Тогда
		//Получаем номер строки в поисковом списке
		НомерЭлементаВСписке=Число(Element.nameProp);
		//Получаем строку по найденному номеру
		ВыбраннаяСтрока=СпПолнПоиска[НомерЭлементаВСписке];
		//Открывает диалоговое окно - форму
		//объекта, ссылка на который присутствует
		//в строке
		ОткрытьЗначение(ВыбраннаяСтрока.Значение);
		//Возвращаем Ложь
		pEvtObj.returnValue=Ложь;
	КонецЕсли;
КонецПроцедуры
Листинг .

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

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

После того, как мы ввели поисковый запрос и нажали кнопку ВыполнитьПоиск, запускается обработчик события нажатия этой кнопки ВыполнитьПоискНажатие(). Здесь свойству СтрокаПоиска объекта СпПолнПоиска присваивается строка, содержащаяся в текстовом поле и, с помощью метода этого объекта ПерваяЧасть() выполняется поиск. Если поиск не дал результатов - мы сообщаем об этом пользователю, если дал - вызываем процедуру ВывестиРезультат(). Эта процедура формирует надпись, сообщающую пользователю о количестве найденных результатов и о просматриваемой в данный момент их порции. Далее, здесь же мы получаем найденные данные в виде HTML-кода и записываем в поле HTML-документа. Завершает эту процедуру вызов процедуры НастроитьДоступностьКнопок().

Процедура НастроитьДоступностьКнопок() позволяет или запрещает использование кнопок Вперед и Назад в зависимости от количества найденных результатов и от порции, просматриваемой в данный момент.

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

И, наконец, процедура РезультатыПоискаonClick() обрабатывает щелчок по гиперссылке на объект в поле HTML-документа. Такой щелчок приводит к открытию формы объекта, заданного в ссылке.

Вот как выглядит работа с нашей формой поиска, рис. 8.6.

Работа с формой поиска

Рис. 8.6. Работа с формой поиска

Если мы внесем изменения в информационную базу, например, введем новый документ, создадим новый элемент справочника или изменим реквизит уже существующего объекта, поисковый индекс потребует обновления. Проведем небольшой эксперимент, который покажет нам, чем грозит несвоевременное обновление индекса. На рис. 8.6 можно видеть, что по ключевому слову "Петр" было найдено физическое лицо Васильев Петр Петрович. Предположим, при вводе данных по этому физическому лицу была допущена ошибка - его, на самом деле, зовут Павел, а ввели Петр. Откроем форму элемента справочника Физические лица (это можно сделать прямо из поискового окна) и изменим имя "Петр" на "Павел". Сохраним элемент и повторим поиск по тому же ключевому слову. Вот, что у нас получится, рис. 8.7.

Поиск по базе с необновленным индексом

Рис. 8.7. Поиск по базе с необновленным индексом

Как видите, в соответствии с индексом сотрудника Васильева П.П. все еще зовут "Петр" - имя (взятое из свойств элемента, то есть - Павел) выведено в качестве подходящего реквизита, хотя на самом деле этот реквизит под условия поиска не подходит.

Решить эту проблему можно вручную - обновлять поисковый индекс в интерактивном режиме уже знакомым вам способом. Это означает, что всякий раз, когда мы решим воспользоваться поиском с использованием нашей поисковой формы, нам, для того, чтобы у нас была уверенность в том, что ищем мы по актуальным данным, нужно обновить поисковый индекс. Это можно забыть сделать, в итоге искать мы будем по устаревшим данным. К счастью, есть программные методы обновления поискового индекса.

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

Добавим в модуль формы отчета ПоискВБазеДанных новую процедуру - ОбновлениеПоисковогоИндекса():

Процедура ОбновлениеПоисковогоИндекса()
		//Если полнотекстовый поиск разрешен
	Если ПолнотекстовыйПоиск.ПолучитьРежимПолнотекстовогоПоиска()=
		РежимПолнотекстовогоПоиска.Разрешить Тогда
		//И если индекс не актуален
		Если Не ПолнотекстовыйПоиск.ИндексАктуален() Тогда
			//Обновить индекс со слиянием и без разбиения на порции
			ПолнотекстовыйПоиск.ОбновитьИндекс(Истина, Ложь);
		КонецЕсли;
	КонецЕсли;
КонецПроцедуры

Здесь мы проверяем, разрешен ли полнотекстовый поиск, актуален ли индекс. Если поиск разрешен, а индекс не актуален, вызываем метод ОбновитьИндекс() объекта ПолнотекстовыйПоиск. Метод принимает два параметра. Первый указывает на то, следует ли провести слияние индексов (в нашем случае мы так и делаем). Второй, установленный в Ложь, отключает порционное индексирование объектов. По умолчанию одна порция индексирования равна 10000 объектов индексирования. Значение Ложь говорит системе о том, что следует проиндексировать все объекты. В небольшой базе, подобной нашей, мы можем так поступать без опасений за производительность решения.

Добавим вызов в начало процедуры ВыполнитьПоискНажатие. Вот как будет теперь выглядеть начало этой процедуры:

Процедура ВыполнитьПоискНажатие(Элемент)
	//Проверка поискового индекса и его обновление
	//если он не актуален
	ОбновлениеПоисковогоИндекса();
…команды, описанные ранее….
КонецПроцедуры

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

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

Евгений Орлов
Евгений Орлов
Кундуз Сабаева
Кундуз Сабаева

Ошибка при выполнении обработчика - 'ОбработкаПроведения'
по причине:
{Документ.НачислениеЗарплаты.МодульОбъекта(45)}: Деление на 0
        Движение.Результат= Движение.ИсходныеДанные*Факт[0].РабочийДень/План[0].РабочийДень;

Андрей Нейман
Андрей Нейман
Россия
Илья Климов
Илья Климов
Россия, Пермь, ПНИПУ, 2013