Реализация полнотекстового поиска с SoftInform Search SDK
Что ж, мы разобрали подробно практически все темы, касающиеся поискового "движка" от компании SoftInform. Сегодня, наконец, завершим этот длинный, но, думаю, достаточно полезный для многих наших читателей разговор. И завершится он рассмотрением той темы, которую мы подняли в предыдущем номере - то есть, завершим рассмотрение процесса создания собственных источников документов.
Напомню, в прошлый раз мы с вами успели увидеть интерфейсные части классов, реализующих интерфейсы, необходимые для создания внешнего источника данных, с которым сможет взаимодействовать SoftInform Search SDK. Но, сами понимаете, что для того, чтобы все нормально работало, одних интерфейсных частей нам явно не хватит. Так что давайте взглянем на то, что у нас в терминах Delphi имеет название Implementation. Думаю, что в разборе всех методов всех классов, которые были приведены в предыдущей части нашего разговора о SoftInform Search SDK, нет такой уж суровой необходимости, потому что в противном случае рассказ об этом программном продукте грозит затянуться ещё.
Для начала посмотрим в сторону метода Configure класса TDSSampleClass. Думаю, что количество разнообразных параметров, которые передаются данному методу при его вызове, способно смутить того, кто еще не слишком привык к работе с SoftInform Search SDK. Но, думаю, что после того, как вы взглянете на листинг 1, все встанет на свои места.
Листинг 1
function TDSSampleClass.Configure(hWnd: LongWord; var AParameters, ACaption, ADescription: WideString; const ATranslate: ISITranslate): WordBool; var AValue: string; begin Result := False; AValue := AParameters; if InputQuery('Delphi Sample DocumentSource config', 'Enter count', AValue) then begin AValue := Trim(AValue); if StrToIntDef(AValue, 0) <= 0 then Exit; Result := True; AParameters := AValue; ACaption := 'Delphi SampleDS'; ADescription := ''; end; end;
В общем-то, код приведенного листинга достаточно прост, но мы с вами все-таки разберем подробнее, что именно там написано. Для начала стоит отметить, что этот метод должен присутствовать в классе только в том случае, если он вообще в принципе конфигурируем, что задается с помощью свойства Configurable. Конечно, в реальных приложениях окно с настройками, которое будет показывать программа, будет заметно отличаться от того предельно простого диалога, который она показывает в примере, приведенном в листинге 1.
Значение, возвращаемое рассмотренной нами функцией, определяет, успешно завершился процесс конфигурации или же изменения, которые были внесены пользователем, не должны быть в конечном итоге приняты (соответственно, и возвращаемое значение равно или не равно нулю). Из параметров, передаваемых функции, наиболее интересен AParameters. Как написано в справке, это "String where document source should store all options and parameters that was configurated". То есть, проще говоря, именно этот параметр фактически и есть результат работы всей функции. Что касается параметра ATranslate, то это просто указатель на экземпляр объекта, реализующего интерфейс ISITranslate и используемого в случае необходимости перевода содержимого окна конфигурации параметров источника документов. В большинстве же случаев трогать его нет никакой необходимости.
Что ж, давайте теперь обратимся к классу TSampleDocument и рассмотрим его конструктор. Его увидеть вы можете в листинге под номером 2.
Листинг 2
constructor TSampleDocument.Create(const ADate: TDateTime; AType: Integer); var SILibTlb: ITypeLib; AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; ATitle: string; begin OleCheck(LoadRegTypeLib(LIBID_SILib, SILibMajorVersion, SILibMinorVersion, 0, SILibTlb)); inherited Create(SILibTlb, ISIDocument); FDate := ADate; FType := AType; DecodeDateTime(FDate, AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond); ATitle := ''; case FType of 0: ATitle := Format('%d days', [DaysBetween(FDate, 0)]); 1: ATitle := Format('%d hours', [HoursBetween(FDate, 0)]); 2: ATitle := Format('%d minutes', [MinutesBetween(FDate, 0)]); end; FHTML := '<HTML><HEAD>'#13#10 + '<TITLE>' + ATitle + '</TITLE>'#13#10 + '</HEAD><BODY><HR>'#13#10 + Format('<B>YEAR:</B> <I>%d</I><BR>'#13#10, [AYear]) + Format('<B>MONTH:</B> <I>%d</I><BR>'#13#10, [AMonth]) + Format('<B>DAY:</B> <I>%d</I><BR>'#13#10, [ADay]); if AType > 0 then FHTML := FHTML + Format('<B>HOUR:</B> <I>%d</I><BR>'#13#10, [AHour]); if AType > 1 then FHTML := FHTML + Format('<B>MINUTE:</B> <I>%d</I><BR>'#13#10, [AMinute]); FHTML := FHTML + '<HR></BODY></HTML>'#13#10; end;
Возможно, сходу листинг может показаться перегруженным малозначительными деталями, особенно "наведением красоты" с помощью HTML-тегов, но на самом деле, мне кажется, здесь этот код не столько мешает, сколько позволяет более полно представить функционал данного метода в реальных приложениях, где, тем не менее, он наверняка будет значительно отличаться от нашего, если можно так сказать, модельного случая.
Для начала мы используем функции OleCheck и LoadRegTypeLib для того, чтобы загрузить библиотеку типов, необходимую для работы с SoftInform Search SDK, и в случае возникновения каких-то непредвиденных осложнений - вывести об этом осмысленную, с точки зрения пользователя, информацию об ошибке. После того, как библиотека типов таки загружена, мы вызываем "родительский" конструктор, ну а затем уже идет как бы логика работы класса, которая и будет отличаться от приведенной в реальных приложениях.
Теперь, я так думаю, будет не лишним поговорить об итераторах, которые мы также видели в прошлый раз. Здесь, опять-таки, рассмотрим именно конструктор, как наиболее ответственную часть всего класса, реализующего итератор для SoftInform Search SDK. Его вы можете увидеть в листинге 3.
Листинг 3
constructor TSampleDocumentItterator.Create(const ANow, ASince: TDateTime; ACount, AType: Integer); var SILibTlb: ITypeLib; begin OleCheck(LoadRegTypeLib(LIBID_SILib, SILibMajorVersion, SILibMinorVersion, 0, SILibTlb)); inherited Create(SILibTlb, ISIDocumentsItterator); FItem := 0; FNow := ANow; FType := AType; FCount := 0; case FType of 0: begin if DaysBetween(ANow, 0) <> DaysBetween(ASince, 0) then FCount := ACount; end; 1: begin if HoursBetween(ANow, 0) <> HoursBetween(ASince, 0) then FCount := ACount; end; 2: begin if MinutesBetween(ANow, 0) <> MinutesBetween(ASince, 0) then FCount := ACount; end; end; end;
В самом начале этого листинга уже знакомый нам по прошлому листингу прием с загрузкой библиотеки типов и обработкой возможных ошибок, и с последующим вызовом inherited-конструктора. Затем идет инициализация всех возможных значений внутренних полей итератора (напомню, что посмотреть на список этих самых внутренних полей вы можете в предыдущей части нашего разговора о SoftInform Search SDK, где мы рассматривали интерфейс всех классов).
Напоследок просто необходимо сказать и о процедуре инициализации всего подключаемого к SoftInform Search SDK модуля. Для этого имеет смысл воспользоваться предоставляемой Delphi секцией инициализации модуля. Текст её (он, как видите, совсем небольшой - в буквальном смысле две строчки) вы можете увидеть в листинге 4.
Листинг 4
initialization TSIPluginClassFactory_Com.Create(ComServer, TDSSampleClass, Class_DSSampleClass, 'DSSampleClass', 'Class of DSSample', ciMultiInstance, tmApartment);
Этот код содержит отсылку к одному достаточно простому классу, который мы с вами не разбирали в предыдущей статье, но который очень просто найти в примерах, идущих в стандартном комплекте поставки с SoftInform Search SDK, так что, думаю, в силу его простоты можно просто сказать, что это и есть инициализация COM-объектов, необходимых для взаимодействия нового источника документов и собственно поискового "движка".
Что ж, думаю, на этом разговор о SoftInform Search SDK можно считать успешно завершенным. Если кому-то он показался излишне подробным и затянутым, приношу свои извинения. Как, впрочем, и тем, кому, наоборот, показалось, что этот программный продукт заслуживает более подробного освещения на страницах "Компьютерных вестей". Так оно, конечно, и есть, но существует масса других интересных инструментов для разработчиков.
Еще раз хочу напомнить, что продукт SoftInform Search SDK имеет российские корни, а значит, если у вас возникнут какие-то вопросы по его использованию, на которые не смогут ответить ни примеры, ни справка (а я могу с уверенностью сказать, что так оно, скорее всего, и будет), вполне можно обращаться к создателям этого программного продукта с просьбой русским языком рассказать и показать, как его использовать. И тех трудностей, которые возникают по мере освоения этого продукта, не нужно пугаться - они неизбежны в любой сфере человеческой деятельности. Все эти сложности, которые на самом деле носят чисто технический характер, с лихвой искупаются теми преимуществами SoftInform Search SDK, которые я долго и методично перечислял в первых статьях, посвященных этому программному продукту. В общем, успешного вам освоения и внедрения SoftInform Search SDK в свои программные продукты!
Вадим СТАНКЕВИЧ,
dreamdrusch@tut.by