22 авг. 2015 г.

Использование полнотекстового поиска при вводе по строке



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

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

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

Проверяем работу кода. Добавляем описание товара, не забываем обновить индекс полнотекстового поиска.


Выполняем ввод по строке в документе:


Поиск отрабатывает, но представление результата не информативно. Возвращаемся в конфигуратор и смотрим, из чего состоит ЭлементПоиска.

Свойство Значение Тип
ЭлементПоиска ЭлементСпискаПолнотекстовогоПоиска ЭлементСпискаПолнотекстовогоПоиска
Значение Стол журнальный СправочникСсылка.Номенклатура
Метаданные Список товаров ОбъектМетаданных
Описание "Описание товара: Стекло, дуб, ковка" Строка
Представление "Стол журнальный" Строка

Перепишем заполнение элементов списка, представление элемента списка формируем из свойств "Представление" и "Описание".

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

Результат уже несколько лучше.


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


Синтаксис-помощник подсказывает, что представлением элемента списка может выступать как Строка, так и ФорматированнаяСтрока. Ок, это как раз наш вариант. Осталось только выделить результат поиска в описании элемента. Используя метод ПолучитьОтображение можно получить отображение результата полнотекстового поиска в HTML и XML представлениях.

<html>
<head>
    <style type="text/css">
        html { overflow:auto; }
        body { margin: 10px; font-family: Arial,sans-serif; font-size: 10pt; overflow:auto; }
        div.main { overflow:auto; height:100%; }
        div.presentation { font-size: 11pt; }
        div.textPortion { padding-bottom: 16px; }
        span.bold { font-weight: bold; }
        a { text-decoration:none; color:#0066CC; }
        a:hover { text-decoration:underline; }
    </style>
</head>
<body>
    <div class="main">
        <div class="presentation">
            <a id="FullTextSearchListItem" href="#" sel_num="0">Список товаров: Стол журнальный</a>
        </div>
        <div class="textPortion">
            Описание товара: <span class="bold" style="background-color:#CCFFCC">Стекло</span>, дуб, ковка
        </div>
    </div>
</body>
</html>

HTML-представление показалось мне неудобным для разбора, поэтому используем второй вариант. XML-представление дает на выходе объект ЧтениеXML, который в свою очередь передается в ПостроительDOM. Окончательный вариант процедуры:

Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
 
 СтандартнаяОбработка = Ложь;
 
 СписокПоиска = ПолнотекстовыйПоиск.СоздатьСписок(Параметры.СтрокаПоиска, 50);
 СписокПоиска.ОбластьПоиска.Добавить(Метаданные.Справочники.Номенклатура);   
 СписокПоиска.ПерваяЧасть();
 
 Если СписокПоиска.Количество() Тогда
  
  ОписаниеСписка = СписокПоиска.ПолучитьОтображение(ВидОтображенияПолнотекстовогоПоиска.XML);
  ЧтениеДОМ = Новый ПостроительDOM;
  ПредставлениеСписка = ЧтениеДОМ.Прочитать(ОписаниеСписка);
  СписокПредставлений = ПредставлениеСписка.ПолучитьЭлементыПоИмени("textPortion");
  МассивПредставлений = Новый Массив;
  
  ШрифтВыделения = Новый Шрифт(,,Истина);
  ЦветВыделения = WebЦвета.Зеленый;
  
  Для Каждого ЭлементПредставления Из СписокПредставлений Цикл
   
   МассивФормата = Новый Массив;
   Для Каждого ДочернийУзел Из ЭлементПредставления.ДочерниеУзлы Цикл
    
    Если ДочернийУзел.ТипУзла = ТипУзлаDOM.Текст Тогда
     ШрифтСтроки = Неопределено;     
     ЦветСтроки = Неопределено;
    ИначеЕсли ДочернийУзел.ТипУзла = ТипУзлаDOM.Элемент Тогда
     ШрифтСтроки = ШрифтВыделения;
     ЦветСтроки = ЦветВыделения;
    КонецЕсли;
    СтрокаФормата = Новый ФорматированнаяСтрока(ДочернийУзел.ТекстовоеСодержимое, ШрифтСтроки, ЦветСтроки);
    МассивФормата.Добавить(СтрокаФормата);
    
   КонецЦикла;
   
   МассивПредставлений.Добавить(МассивФормата);
   
  КонецЦикла;
   
  ДанныеВыбора = Новый СписокЗначений;
  НомерЭлемента = 0;
  Для Каждого ЭлементПоиска Из СписокПоиска Цикл
   
   НомерЭлемента = НомерЭлемента + 1;   
   Значение = ЭлементПоиска.Значение;
   Если Значение.ЭтоГруппа Тогда
    Продолжить;
   КонецЕсли;
   
   ПредставлениеЭлемента = Новый ФорматированнаяСтрока(ЭлементПоиска.Представление + ", ", МассивПредставлений[НомерЭлемента - 1]);
   ЭлементСписка = ДанныеВыбора.Добавить(ЭлементПоиска.Значение, ПредставлениеЭлемента);
   
  КонецЦикла;
  
 КонецЕсли;
 
КонецПроцедуры

Проверяем, результат получается вполне сносный.






3 комментария:

  1. Как вариант:
    //ОбработкаПолученияДанныхВыбора - Произвольный запрос и формирование "ДанныеВыбора"

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


    Функция ВернутьФорматированнуюСтрокуПоиска(СтрокаДляРазбора,СтрокаПоиска) Экспорт
    СтрокаЗамены = "";
    Для стр = 1 По СтрДлина(СтрокаПоиска) Цикл
    СтрокаЗамены = СтрокаЗамены + "@";
    КонецЦикла;

    //РазложитьСтроку - Функция БСП из "СтроковыеФункцииКлиентСервер"
    МассивСтрок = РазложитьСтрокуВМассивСлов(СтрЗаменить(СтрокаДляРазбора,СтрокаПоиска,СтрокаЗамены+"_"+СтрокаЗамены),СтрокаЗамены);

    //СобираемСтроку
    ФормСтрока = Новый Массив;
    Для каждого СтрокаИзМассива Из МассивСтрок Цикл
    Если СтрокаИзМассива = "_" Тогда
    ФормСтрока.Добавить(Новый ФорматированнаяСтрока(СтрокаПоиска,Новый Шрифт(,,Истина),WebЦвета.ЗеленыйЛес));
    Иначе
    ФормСтрока.Добавить(Новый ФорматированнаяСтрока(СтрокаИзМассива))
    КонецЕсли;
    КонецЦикла;
    Возврат Новый ФорматированнаяСтрока(ФормСтрока);
    КонецФункции

    ОтветитьУдалить
    Ответы
    1. Функция чуствительна к регистру.

      Удалить
    2. Может кому пригодится, функцию написал независимую от регистра


      Функция ВернутьФорматированнуюСтрокуПоиска(СтрокаДляРазбора, СтрокаПоиска) Экспорт
      СтрокаВВерхнемРегистре = Врег(СтрокаДляРазбора);
      СтрокаПоискаВВерхнемРегистре = Врег(СтрокаПоиска);

      ФормСтрока = Новый Массив;
      Для Стр = 1 По СтрЧислоВхождений(СтрокаВВерхнемРегистре, СтрокаПоискаВВерхнемРегистре) Цикл
      Индекс = Найти(СтрокаВВерхнемРегистре, СтрокаПоискаВВерхнемРегистре);
      Если НЕ Индекс = 1 Тогда
      ФормСтрока.Добавить(Новый ФорматированнаяСтрока(Сред(СтрокаДляРазбора,1,Индекс-1)));
      КонецЕсли;
      ФормСтрока.Добавить(Новый ФорматированнаяСтрока(Сред(СтрокаДляРазбора, Индекс, СтрДлина(СтрокаПоиска)), Новый Шрифт(,,Истина),WebЦвета.ЗеленыйЛес));
      СтрокаДляРазбора = Прав(СтрокаДляРазбора, СтрДлина(СтрокаДляРазбора) - (Индекс + (СтрДлина(СтрокаПоиска)-1)));
      СтрокаВВерхнемРегистре = Прав(СтрокаВВерхнемРегистре, СтрДлина(СтрокаВВерхнемРегистре) - (Индекс + (СтрДлина(СтрокаПоиска)-1)));
      КонецЦикла;
      ФормСтрока.Добавить(Новый ФорматированнаяСтрока(СтрокаДляРазбора));

      Возврат Новый ФорматированнаяСтрока(ФормСтрока);
      КонецФункции

      Удалить