My FAQ,最新最全的IT技术FAQ
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 未整理篇 | 技术讨论
  当前位置: > IBM专区 > DB2 > 集成
理解 WebSphere Information Integrator OmniFind Edition 的示例搜索应用程序
作者:Dana Morris 时间:2005-08-03 17:13 出处:互连网 责编:小渔
              摘要:企业可以使用 WebSphere® Information Integrator OmniFind™ Edition 开发高度定制的搜索应用程序,从而最好地满足企业独特的需要、要求以及混合信息源。本文将提供 WebSphere Information Integrator OmniFind Edition 示例搜索应用程序的技术性概括。还将介绍该产品的总体设计、一些重要的类和方法,以及一些基本的执行例程。

级别: 中级

Dana Morris
顾问软件工程师, IBM
2005 年 7 月 25 日

企业可以使用 WebSphere® Information Integrator OmniFind™ Edition 开发高度定制的搜索应用程序,从而最好地满足企业独特的需要、要求以及混合信息源。本文将提供 WebSphere Information Integrator OmniFind Edition 示例搜索应用程序的技术性概括。还将介绍该产品的总体设计、一些重要的类和方法,以及一些基本的执行例程。

简介
目前,许多公司都要面对不断增加的大量信息。缺乏适当的用来访问和管理企业信息资产的系统可能会给企业带来损害。如果在需要的时候无法找到密切相关的信息,那么将导致企业做出错误的决策、错失良机、浪费时间和金钱。

WebSphere Information Integrator OmniFind Edition(WebSphere II OmniFind Edition)是为支持在种类繁多的内容源上进行企业级搜索而设计的。WebSphere Information Integrator 新的企业搜索服务器提供了高质量的、可伸缩的、安全的、形式自由的(free-form)本文搜索。WebSphere II OmniFind Edition 提供了大量功能,用于从单一访问点(single point of access )搜索不同的业务信息集合,以及在文档数量扩展到数百万和用户数量扩展到数千位时,以亚秒级(subsecond)的响应时间交付高度相关的搜索结果。

在安装 WebSphere® Information Integrator OmniFind™ Edition(WebSphere II OmniFind Edition)的过程中,会将一个示例搜索 Web 应用程序自动安装到本地 WebSphere® Application Server 中。这个预先部署的应用程序的首要用途就是验证 WebSphere II OmniFind Edition 管理员为进行搜索而创建的集合。该应用程序还可以充当一个实际示例,以便客户使用该示例作为他们自己搜索接口的基础。本文将深入查看该搜索 Web 应用程序。在完成本文之后,您应该掌握了足够多的信息,能够成功地定制该搜索应用程序,从而满足企业的特定需求。

先决条件
本文假定您具有下列技术的相关知识:

  • Apache Jakarta Struts v1.1
  • Apache Jakarta Struts Tiles
  • Java™ Servlet Specification Level 2.3
  • Java Server Pages(JSP)
  • IBM Search and Indexing API(SIAPI)version 1.4
关于这些技术的进一步信息,请参阅本文后面的 参考资料 一节。

 

概述
在研究该示例搜索应用程序的技术细节之前,理解终端用户如何与该应用程序进行交互是很重要的。图 1 描绘了将该应用程序装入 Web 浏览器时用户首先看到的画面。

图 1. Search 页面
Search 页面

正如图 1 中可以看到的,该页面包含三个主要元素:

  • 标题
  • 导航工具栏
  • 主体

 

工具栏包含了 7 个链接,其中高亮显示了活动链接:

  • Search:打开主页面,该页面用于输入针对一个或多个集合的查询。
  • Category Tree:打开一个页面,该页面用于搜索和浏览单个集合中指定类别的文档。
  • Options:打开一个页面,在这个页面中,用户可以配置用于创建查询和获得搜索结果的选项。
  • My Profile:如果启用了 WebSphere Global Security,则该页面允许用户在查询保密集合时输入要提交的凭证(credential)。
  • Logoff:如果启用了 WebSphere Global Security,则该链接允许用户退出这个搜索应用程序
  • Help:启动 DB2 Information Center 中一个上下文敏感的帮助页面。
  • About:启用一个包含了具有产品名和版权信息的图像的页面。

 

Search 页面
如果我们将关注点集中在 Search 页面(参见 图 1)的主体(Body)部分,您将注意到用户只需要与两项进行交互。如果用户不选择要搜索的集合,那么在默认情况下,该搜索应用程序将搜索所有可用的集合。用户可以通过选择搜索一个或多个指定集合来限制查寻范围。为了提交查询,用户需要输入查询项并单击 Search 按钮。其他所有查询和搜索结果控件的默认值是为用户自动指定的,以便用户使用起来尽可能的简单。

Category Tree 页面
如果用户单击工具栏上的 Category Tree 链接,那么他们将进入一个与 Search 页面极其相似的页面(参见 图 2)。Category Tree 页面允许用户搜索某一个集合,因为类别树(category tree)是按照每个集合指定的。Search 和 Category Tree 页面之间的另一个不同之处在于 Category Tree 页面的左下角出现了树控件。该树允许用户浏览分配给树中每个类别的文档,这类似于用户如何定位 Windows® Explorer。如果用户从树中选择一个类别,那么该页面的右侧会显示属于该类别的文档。

图 2. Category Tree 页面
Category Tree 页面

Options 页面
如果用户需要修改查询的执行方式或结果的返回方式,那么可以单击工具栏中的 Options 链接(参见 图 3)。该页面包含几个用户可以修改的选项。在对该页面进行了修改之后,用户可以单击 Apply 按钮。这将验证用户的选项,然后保存这些选项。如果用户再次单击 Search 链接,则会用新的查询选项重新执行前面的搜索。

图 3. Options 页面
Options 页面

深入分析
该搜索应用程序是用 Apache Jakarta Struts 框架设计的。Struts 框架遵循 Model-View-Controller(MVC)设计模式。为了理解该搜索应用程序的操作,首先需要了解 Struts 框架。下一节将在 Struts 框架的环境中研究 WebSphere II OmniFind Edition 示例搜索应用程序的设计。

模型
模型(Model)层包含系统状态或业务逻辑 bean。本例中,业务逻辑是直接在 SIAPI Query 和 ResultSet 对象中维护的。因为该搜索应用程序的目的是允许终端用户与 WebSphere II OmniFind Edition 搜索服务器进行交互,所以 SIAPI 类可以直接用于应用程序中,以充当 bean。本页面中还有一些用来帮助用户进行交互的附加的 bean 和类。出于本文的目的,我们只特别关注其中某一项,即 ISearchHelper 接口。

ISearchHelper 接口定义了一组将要实现的必需的搜索页面操作。这个开箱即用(out of the box)的搜索应用程序包含了一个支持 SIAPI 的 ISearchHelper 接口实现。通过提供一个接口类,可以支持该搜索应用程序所需的搜索操作的其他变体。例如,我们可能需要增强该搜索应用程序,以便对另一个不支持 SIAPI 接口的搜索产品进行搜索。

视图
应用程序的视图(View)层是使用 Struts Tiles 和 JavaServer Pages(JSP)构建的。已经为该应用程序创建了多种平铺布局(tiles layout):

  • baseLayout.jsp:提供 Search 页面使用的基本布局。包含 3 个部分:标题、工具栏和主体。
  • portalBaseLayout.jsp:提供将应用程序执行为 WebSphere Portal portlet 时所使用的基本布局。包含 2 各部分:工具栏和主体。
  • toolbarLayout.jsp:提供用于 baseLayout 和 portalBaseLayout 的工具栏区域的基本布局。
  • treeLayout.jsp:提供类别浏览页面上所使用的类别树的布局。

 

当用户单击工具栏上的一个特定链接时,将加载一个 JSP 页面来表示主体部分:

  • search.jsp:提供搜索主页面。
  • categoryTree.jsp:提供用于搜索和浏览类别树的页面。
  • options.jsp:提供用于控制查询和结果选项的页面。

 

search.jsp 和 categoryTree.jsp 页面包括其他几个 JSP 页面。因为这两个 JSP 页面共享许多相同的控件和属性,所以要创建其他几个 JSP 页面来包含这两个页面的公共逻辑,避免代码出现复制和更好地维护这些页面。通过使用 jsp:include 标签,可以将下列附加的 JSP 页面加载到父 JSP 页面中:

  • commands.jsp:提供用于动态控制搜索结果的显示方式的命令。
  • queryPrompt.jsp:提供用于选择搜索或浏览集合的查询输入框、下拉框或复选框。
  • messages.jsp:交付结果集中返回的所有消息。这些消息可以是错误、警告或信息消息。
  • quickLinks.jsp:交付搜索结果中返回的所有预定义结果(快速链接)。该页面仅在 search.jsp 中使用。
  • resultsHeaderFooter.jsp:显示搜索结果的数目和用于定位结果页面的控件。
  • searchResults.jsp:交付真正的搜索结果。
  • spellCorrections.jsp:交付返回的所有拼写建议。该页面仅用于 search.jsp。
  • synonymExpansions.jsp:交付返回的所有同义词扩展。该页面仅用于 search.jsp。

 

Controller 组件
正如 Struts User 指南中所解释的,控制器(controller)的工作就是处理用户的请求,并确定使用什么样的视图返回给终端用户。WebSphere II OmniFind Edition 搜索应用程序是用一个 ActionForm 和多个 Struts Action 类设计的。所有 Action 类都将扩展另一个名为 BaseAction 的类,而该类又依次扩展 Struts Action 类。在研究 Action 类之前,让我们首先查看 BaseAction 类提供了哪些服务。

BaseAction 用于提供大多数逻辑和辅助(helper)方法,而其他 Action 类需要用这些方法来处理用户请求。添加附加父类的目的是减小复杂性,以及共享 Action 类之间的公用功能。增加所带来的好处是为应用程序将来的扩展提供了良好框架。BaseAction 类提供了下列功能:

  • 与 ActionForm 属性进行交互。
  • 初始化 SIAPI 和 National Language Support(NLS)服务。
  • 提供公共操作,例如刷新集合列表、清除搜索结果和处理查询。
  • 提供对 Apache Commons Logging 服务的访问。

 

每个 Action 类都将实现 Struts Action 类的 execute 方法。无论用户何时执行需要他们提交页面的操作,execute 方法都是首要入口点。通过调用 BaseAction initialize 方法,execute 方法首先确保已经装入了必需的资源;这些资源包括 SIAPI 服务、可用的搜索(集合)列表和 NLS 相关的实用程序

每个 Action 提取一个 ActionForm 变量调用命令,该命令表示用户在搜索应用程序上调用了哪些操作。该命令属性的可能值因用户当前交互页面的不同而有所不同。大多数操作将调用一些对搜索服务器执行的 SIAPI 操作。用户可以从 Search 或 Category Tree 页面调用的一些可能操作是:

  • clearResults:清除请求的当前查询及其结果。
  • refresh:刷新可用的搜索(集合)列表。
  • search:提交基本的搜索请求。
  • prev:检索搜索结果的前一页面。
  • next:检索搜索结果的后一页面。
  • treeNodeSelect:选择类别树中的节点。
  • treeNodeExpand:展开类别树中的特定节点。

 

execute 方法使用一系列 IF 语句来确定要执行的操作以及将响应发送给哪个视图。上面所列的每个命令都有一个从 execute 方法中调用的相应 action 方法。这些 action 方法将执行用来执行用户请求操作的必要步骤,然后通过使用 struts-config.xml 文件中定义的 ActionMappings 将响应发送给适当的视图。

例如,让我们看一看用于刷新命令的代码(参见 清单 1)。该方法将通过调用 loadFederatorCollections 方法,加载从 SIAPI RemoteFederator 获得的集合列表。然后,它将向 Struts ActionErrors 类添加所有错误,最后将响应发送给适当的视图。

清单 1. refreshAction 方法

            protected ActionForward refreshAction(
            ActionMapping mapping,
            ActionForm form,
            ActionErrors errors,
            HttpServletRequest request,
            String targetPage) {
            // load the Remote Federator and it's list of assigned collections
            loadFederatorCollections(errors, request);
            if (!errors.isEmpty()) {
            saveErrors(request, errors);
            }
            return mapping.findForward(targetPage);
            }
            

loadFederatorCollections 方法(参见 清单 2)首先从会话中提取 ISearchHelper 对象。如果在会话中找到 Helper 类,那么就调用 ISearchHelper.loadRemoteFederator 方法来加载搜索服务器中的 SIAPI RemoteFederator 对象。最后,通过分配给 RemoteFederator 的 SIAPI CollectionInfo 对象数组创建一个 Collection valueObjects 数组。然后将该 Collection 数组存储在形式变量“collections”中。CollectionInfo 对象的数组将加载在 queryPrompt.jsp 页面上,以显示给终端用户。

清单 2. loadFederatorCollections 方法

            DynaActionForm theForm = (DynaActionForm) form;
            ISearchHelper helper =
            (ISearchHelper) request.getSession().getAttribute(
            "SearchHelper");
            if (helper != null) {
            // get the federator from the server
            helper.loadRemoteFederator();
            // get the list of collections from the federator and store
            // the returned list in the form
            CollectionInfo[] infos = helper.getCollectionInfos();
            Vector collections = new Vector();
            for (int i = 0; i < infos.length; i++) {
            Collection collection =
            new Collection(infos[i].getID(), infos[i].getLabel());
            collections.add(collection);
            }
            Collection[] collectionObjs =
            new Collection[collections.size()];
            collectionObjs =
            (Collection[]) collections.toArray(collectionObjs);
            theForm.set("collections", collectionObjs);
            }
            

该搜索应用程序中还使用了其他几个类,以提供不同的功能。所有这些类定义的上方都有注释,提供了关于该类的用途的细节。下面是这些类所提供的不同子包和服务的要点概括:

  • com.ibm.es.searchui.actions:包含 BaseAction 类以及扩展 BaseAction 的其他所有 action 类。
  • com.ibm.es.searchui.filters:Servlet 筛选器类,该类将请求编码设置为 UTF-8 格式,以支持用所有语言编写的查询字符串输入。在 WebSphere Portal 中运行应用程序时,不需要使用这个包。
  • com.ibm.es.searchui.helpers:这个包有三个类,其中两个(TreeControl 和 TreeControlNode)提供用来表示类别树和 ISearchHelper 接口的包装器类。
  • com.ibm.es.searchui.helpers.siapi:包含 ISearchHelper 接口的 SIAPI 实现。
  • com.ibm.es.searchui.tags:一系列的小型 Tag 库,当应用程序在标准的 WebSphere Application Server 部署中运行时,这些库模拟 WebSphere Portal EncodeNamespaceTag,并为诸如拼写建议、同义词扩展、文档日期和文档计分(scoring)等特定类型的显示信息提供格式化支持。
  • com.ibm.es.searchui.validation:提供一个用来验证用户输入的类,以获得“每页结果数目”。
  • com.ibm.es.searchui.valueObjects:包含 ResultLanguage 类,用于存储每种受支持的语言及其相应的转换后的字符串表示。例如:en_US 是受支持的语言,而“English - United States”则可能是显示给用户的转换后的字符串。

 

所检查的搜索执行
现在,让我们检查典型的搜索执行场景。对于该场景,假定用户保留所选择的默认集合,输入一个简单的查询字符串,然后单击 Search 按钮。首先,该请求输入 SearchAction 类中的 execute 方法。在处理搜索之前,要调用 SearchValidation 类来确保用户为每页的结果数目输入了一个有效值。

一旦该请求通过了验证,随后将调用 processQueryAction 方法。该方法立即从会话中提取 ISearchHelper 对象。如果出现 ISearchHelper 对象,则提取 ActionForm 参数并将它存储在 HashMap 中。这个 HashMap 将传递给 ISearchHelper.buildQueryString 方法(参见 清单 4),该方法将构造一个有效的 SIAPI 查询字符串,以便将其追加到用户的查询项中。例如,如果客户需要将结果限制为用日语编写的文档,那么该查询将包含一个附加的查询项“^$language::ja”。

清单 3. BaseAction processQueryAction 代码片断

            HashMap parameters = getFormValues(form);
            parameters.put(
            "categoryId",
            request.getParameter("categoryId"));
            parameters.put(
            "siteSearch",
            request.getParameter("siteSearch"));
            parameters.put(
            "UserSecurityContent",
            (String) request.getSession().getAttribute(
            "UserSecurityContent"));
            // build the query string
            String queryString = helper.buildQueryString(parameters);
            Query query = helper.createQuery(queryString, parameters, calculatePageControl(form));
            if (query != null
            && query.getText() != null
            && query.getText().length() > 1) {
            ResultSet results = null;
            // if the user is interacting with the category tree
            // then they can only select one collection and it will
            // be in the collectionSelected property
            if (targetPage.compareToIgnoreCase("categoryTreePage") == 0) {
            String[] selected = new String[] {(String) theForm.get("collectionSelected")};
            results = helper.search(query, selected);
            } else {
            // otherwise, walk the list of collections and see which ones the user
            // selected for searching
            Collection[] collections = getCollections((DynaActionForm) form, request);
            if (collections != null) {
            Vector ids = new Vector();
            for (int i = 0; i < collections.length; i++) {
            if (collections[i].isSelected()) {
            ids.add(collections[i].getID());
            }
            }
            String[] selectedIDs = new String[ids.size()];
            selectedIDs = (String[]) ids.toArray(selectedIDs);
            results = helper.search(query, selectedIDs);
            } else {
            // default to searching all collections in the
            // RemoteFederator
            results = helper.search(query, null);
            }
            }
            storeQueryResponse(results, query, request, form);
            }
            
清单 4. SiapiHelper buildQueryString 代码片断

            String query = (String) parameters.get("queryString");
            String site = (String) parameters.get("siteSearch");
            String categoryId = (String) parameters.get("categoryId");
            String[] languages = (String[]) parameters.get("resultLanguages");
            String[] sources = (String[]) parameters.get("documentSources");
            String[] types = (String[]) parameters.get("documentTypes");
            String[] scopes = (String[]) parameters.get("scopes");
            if (query != null && query.length() > 1) {
            // check if this is a query to search more from
            // the same site
            if (site != null) {
            query = query + " samegroupas:" + site;
            }
            // if the user selected any scopes to limit
            // the query, add them
            if (scopes != null && scopes.length > 0) {
            for (int i = 0; i < scopes.length; i++) {
            query = query + " ^$scopes::" + scopes[i];
            }
            }
            // return only documents that match the selected languages
            if (languages != null && languages.length > 0) {
            for (int i = 0; i < languages.length; i++) {
            query = query + " ^$language::" + languages[i];
            }
            }
            // return only documents of the specified types
            if (types != null && types.length > 0) {
            for (int i = 0; i < types.length; i++) {
            query = query + " ^$doctype::" + types[i];
            }
            }
            // return only documents that match the specified source (crawler) types
            // examples:  UNIXFS, WINFS, WEB, etc
            if (sources != null && sources.length > 0) {
            for (int i = 0; i < sources.length; i++) {
            query = query + " ^$source::" + sources[i];
            }
            }
            }
            

在成功构造了 SIAPI 查询字符串之后,将调用 ISearchHelper.createQuery 方法(参见 清单 5),以便通过查询字符串并添加用户的其他查询选项来创建真正的 SIAPI Query 对象。附加查询选项的实例是一些设置,例如是否在查询结果中返回拼写建议或包含同义词扩展。在有效的 SIAPI Query 对象中,将调用 ISearchHelper.search 方法对搜索服务器实际执行搜索请求。然后,将响应发送给 processSearchResponse 方法。

清单 5. SiapiHelper createQuery 代码片断

            // create the query that passes in the query string
            Query query = searchFactory.createQuery(queryString);
            query.setRequestedResultRange(start, range);
            // if there was a previous query, then we need to maintain the
            // SearchState
            if (state != null) {
            query.setSearchState(state);
            }
            // set the query language
            if (queryLanguage != null && queryLanguage.length() > 1) {
            query.setQueryLanguage(queryLanguage);
            }
            // if there are any security tokens, add them here
            // In 8.2 Fixpack 2, this is only relevant for collections that
            // contain documents from Domino databases
            if (aclConstraints != null && aclConstraints.length() > 0) {
            // if you wanted to also support additional security tokens
            // that were configured when creating crawlers
            // refer to the API guide and the SIAPI Javadoc for more
            // information on the syntax of the setACLConstraints method's
            // query string parameter
            query.setACLConstraints(
            @SecurityContext::' + aclConstraints + ');
            }
            // should predefined results (quick links) be returned with the results?
            if (predefinedResults.compareToIgnoreCase("Yes") == 0) {
            query.setPredefinedResultsEnabled(true);
            } else {
            query.setPredefinedResultsEnabled(false);
            }
            // should results from the same site be collapsed?
            if (siteCollapsing.compareToIgnoreCase("Yes") == 0) {
            query.setSiteCollapsingEnabled(true);
            } else {
            query.setSiteCollapsingEnabled(false);
            }
            // should spelling corrections be returned with the results?
            if (spellCorrections.compareToIgnoreCase("Yes") == 0) {
            query.setSpellCorrectionEnabled(true);
            } else {
            query.setSpellCorrectionEnabled(false);
            }
            // should synonym expansions be returned with the results?
            if (synonymExpansions.compareToIgnoreCase("Automatic") == 0) {
            query.setSynonymExpansionMode(Query.SYNONYM_EXPANSION_AUTOMATIC);
            } else {
            query.setSynonymExpansionMode(Query.SYNONYM_EXPANSION_OFF);
            }
            

storeQueryResponse 方法(参见 清单 6)首先将 SIAPI Query 和 ResultSet 对象存储到请求中,以便视图(View)可以提取它们,将它们交付给用户。此外,该方法还要计算显示给用户的当前结果的范围(例如:21-30)。从 ResultSet 中提取 SiapiMessage 对象,并在 messages.jsp 文件中将它们显示给终端用户。最后,将响应发送给适当的视图。

清单 6. SiapiHelper createQuery 代码片断

            DynaActionForm theForm = (DynaActionForm) form;
            // store the query and results in the request
            request.setAttribute("results", results);
            request.setAttribute("query", query);
            // determine the page variables to use for display
            // to the user
            int range =
            query.getFirstRequestedResult() + query.getNumRequestedResults();
            if (range > results.getAvailableNumberOfResults()) {
            range = results.getAvailableNumberOfResults();
            request.setAttribute("isLastPage", "true");
            } else {
            request.setAttribute("isLastPage", "false");
            }
            request.setAttribute("displayRange", Integer.toString(range));
            request.setAttribute(
            "firstResult",
            Integer.toString(query.getFirstRequestedResult() + 1));
            // store any messages from the ResultSet into the messages
            // textarea
            List messages = results.getMessages();
            if (messages != null && messages.size() > 0) {
            StringBuffer sb = new StringBuffer();
            Iterator iter = messages.iterator();
            while (iter.hasNext()) {
            SiapiMessage message = (SiapiMessage) iter.next();
            // load the message in the user's locale
            try {
            sb.append(message.getMessage(true, request.getLocale()));
            } catch (SiapiException e) {
            // failed to load the message, display the error for failing
            // to load the message instead
            sb.append(e.getMessage());
            }
            sb.append("\n");
            }
            // store the messages to the textarea on the messagesFooter.jsp
            theForm.set("messages", sb.toString());
            }
            

结束语
在完成本文之后,您应该对 IBM WebSphere Information Integrator OmniFind Edition 示例搜索应用程序的基本设计和执行有了一个大致的理解。该搜索应用程序意图成为一个全功能的示例应用程序,以演示该产品所提供的强大搜索功能。通过使用本文和产品编程指南(Programming Guide and API Reference for Enterprise Search),您可以使用该示例应用程序作为设计您自己定制的用户接口的基础。

下载

*Product documentation: WebSphere Information Integrator OmniFind Edition Programming Guide and API Reference for Enterprise Search

关于作者
作者照片Dana Morris 是 IBM Software Group 中的 Search Technology 部门的软件工程师。在过去的 5 年里,他一直积极参与 IBM 搜索技术方面的工作。可以通过 damorris@us.ibm.com 与 Dana 联系。

 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 myfaq.com.cn All rights reserved. www.myfaq.com.cn 版权所有