Реализация полнотекстового поиска с SoftInform Search SDK

Часть третья: практика, практика и ещё раз практика

Продолжаем знакомство с замечательным продуктом, позволяющим программистам реализовывать в своих приложениях возможности полнотекстового поиска информации среди базы документов, - SoftInform Search SDK. И сегодня будем заниматься чистой практикой - то есть, рассматривать код, который отвечает за выполнение различных поисковых задач.


Собственно поиск

В прошлый раз мы, если помните, остановились на установке соединения с локальным или удаленным поисковым сервером. Это достаточно важная часть взаимодействия с SoftInform Search SDK, но, как вы наверняка сами догадываетесь, с этого все только начинается - ведь, соединившись с сервером, мы можем посылать ему различные запросы, которые как раз и являются основным способом рассказать серверу, что мы хотим от него выполнения какого-либо действия, так или иначе связанного с поиском. Сейчас посмотрим, каким именно образом можно осуществить поиск, соответствующий запросу пользователя. Вы можете увидеть соответствующий код в листинге 1.

Листинг 1

 
procedure PerformSearchRequest(Query: string);
var
 SiTextRequest: ISITextSearchRequestEx;
 SiDocLst: ISIDocumentsList;
 Msg: string;
 i: Integer;
begin
// Создаём текстовый запрос к серверу
 SiTextRequest := SiReqGen.CreateTextSearchRequestEx;
 SiTextRequest.Query := Query;
// Точное соответствие запросу
 SiTextRequest.WordOption := siwoExact;
// Подключаем случайную базу синонимов
 Randomize;
 SiTextRequest.SynonymDatabase := SiSynDb.GetDatabase(
  SiDBs.GetDatabaseList.Items
  [Random(SiDBs.GetDatabaseList.Items.Count)], true);
// Собственно поиск
 SiDocLst := SiIndex.ComplexSearch(SiTextRequest, nil);
// Показываем результаты поиска
 if SiDocLst.Count > 0 then begin
  Msg := 'Найдено документов: ' + IntToStr(SiDocLst.Count) + #13#10;
  for i := 0 to SiDocLst.Count -1 do begin
   Msg := Msg +'Документ ' + IntToStr(i) + ': '
    + SiDocLst.DocumentID[i].FormatID(sidfFullFormat) + #13#10;
  end;
 end
 else begin
  Msg := 'Ничего не найдено.';
 end;
 ShowMessage(Msg);
end;

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

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

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

Как видите, процесс поиска с использованием SoftInform Search SDK не вызывает проблем, хотя мы и рассмотрели простейший вариант. Однако, смею вас заверить, использование SoftInform Search SDK для поиска - занятие не слишком сложное.


"Сложный" поиск

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

К сожалению, если при работе с SoftInform Search SDK вам потребуется реализовать формальные запросы хотя бы в виде исключения результатов с определёнными словами или с обязательным нахождением пары слов, встречающихся в тексте именно рядом друг с другом, весь процесс работы с такими запросами от начала и до победного конца на сервер переложить не удастся. Конечно, механизмы для подобного поиска в SoftInform Search SDK есть, но первоначально запрос нужно обрабатывать на стороне клиента. Впрочем, здесь есть и плюс: можно гибко менять синтаксис формальных запросов, делая его более простым для пользователя. Например, это может оказаться весьма полезным в процессе локализации приложения: вы можете сделать синтаксис для англоязычных пользователей близким к английскому языку, для русскоязычных - к русскому, и т.д. В листинге 2 демонстрируется применение отрицания в формальном запросе. Для простоты восприятия основные моменты, показанные в предыдущем листинге, опущены.

Листинг 2

i := Pos('[NOT]', Query);
// Проверяем наличие необходимой лексемы в запросе
if i > 0 then begin
// Отбрасываем [NOT] и формируем запрос по оставшемуся тексту
 s := Copy(Query, i + 5, MaxInt);
 SiUnaryRequest := SiReqGen.CreateComplementSearchRequest;
// Искомый текст будет содержаться в подзапросе
 SiTextRequest := SiReqGen.CreateTextSearchRequestEx;
 SiTextRequest.Query := s;
 SiTextRequest.WordOption := siwoExact;
 SiUnaryRequest.SubRequest := SiTextRequest;
end;

Как легко понять из второго по счету листинга, мы ищем документы, в которых не присутствует запрашиваемый пользователем текст. Сначала мы обрабатываем синтаксис (здесь это заключается в том, что мы отбрасываем "[NOT]", но, сами понимаете, что в реальном поиске вам нужно будет эту лексему сначала найти и распознать, а только потом обрабатывать - впрочем, это уже несколько выходит за рамки данной статьи). Именно поэтому мы используем унарный запрос с помощью экземпляра класса, реализующего интерфейс ISIUnarySearchRequest. Если же мы будем обрабатывать формальный запрос, который состоит из двух частей, то этот вариант нам уже не совсем подойдет - вернее даже, совсем не подойдет. Для этого нам нужен интерфейс ISIBinarySearchRequest.

Пример поиска с использованием ISIBinarySearchRequest вы можете увидеть в листинге 3. В принципе, его код не очень сильно отличается от листинга 2, но, думаю, все равно будет достаточно полезно посмотреть, каким именно образом реализуется поиск с помощью бинарных запросов в SoftInform Search SDK.

Листинг 3

i := Pos('[AND]', Query);
// Снова проверяем наличие необходимой лексемы
if i > 0 then begin
// Разделяем наш запрос на два подзапроса
 SubQuery1 := Copy(Query, 1, i - 1);
 SubQuery2 := Copy(Query, i + 5, MaxInt);
 SiBinaryRequest := SiReqGen.CreateIntersectionSearchRequest;
 SiTextRequest := SiReqGen.CreateTextSearchRequestEx;
 SiTextRequest.Query := SubQuery1;
 SiTextRequest.WordOption := siwoExact;
 SiBinaryRequest.SubRequestA := SiTextRequest;
 SiTextRequest := SiReqGen.CreateTextSearchRequestEx;
 SiTextRequest.Query := SubQuery2;
 SiTextRequest.WordOption := siwoExact;
 SiBinaryRequest.SubRequestB := SiTextRequest;
end;

Итак, что мы видим в этом листинге? Сначала мы ищем в тексте запроса ключевую лексему "[AND]", которая свидетельствует о том, что это действительно бинарный запрос - то есть в результатах должны присутствовать слова, имеющиеся в обеих частях запроса. После того, как распознали "хитры" запрос, разделяем его на две части, которые потом становятся составными частями запроса в нашем ISIBinarySearchRequest. В общем и целом, на этом действительно отличающаяся от листинга 2 часть нашего примера заканчивается, потому что дальше начинаются технические детали, связанные с тем, что бинарный запрос ровно в два раза сложнее унарного и в связи с этим требует несколько, скажем так, более тщательной подготовки перед передачей себя, любимого, серверу.


Промежуточные итоги

Хотя наш разговор о таком интересном и, безусловно, полезном программном продукте, как SoftInform Search SDK, еще далеко не закончен, некоторые промежуточные выводы мы сделать, я думаю, вполне можем. Как видите, полнотекстовый поиск с использованием SearchInform SDK - не самая сложная вещь, по крайней мере, что касается обычных простых или составных формальных запросов. В следующий раз, кстати, мы планируем продолжить разговор о них.

Вадим СТАНКЕВИЧ,
dreamdrusch@tut.by

Версия для печатиВерсия для печати

Номер: 

22 за 2010 год

Рубрика: 

Software
Заметили ошибку? Выделите ее мышкой и нажмите Ctrl+Enter!