IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Web development | Open source  >

使用 Rico JavaScript 库、ColdFusion MX 7 和 Windows Indexing Service 构建一个启用 Ajax 的搜索页面

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

样例代码


级别: 中级

Philippe Randour (philippe@randour.net), 软件和系统工程师, Krauthammer International

2008 年 1 月 18 日

在网站或 Intranet 中有大量的可用信息,这些信息需要特殊的工具来快速方便地索引其内容并提供对它的访问。本文学习如何在耦合了 ColdFusion 和 Microsoft® Windows® Indexing Service 等成熟技术的 Ajax 库的帮助下实现上述操作并提供最新的搜索工具。

几个月前,我和一些同事讨论关于将一个搜索工具添加到 Intranet 中(作为一个向导)并将其扩展到企业网站中的可能性。此提议的主要目标之一是弄清楚哪些访问者在查找 Web 内容并相应地改变了内容。我们中的一些人构想了一个完全自定义的解决方案(我不是在开玩笑)。他们建议在数据库服务器上构建表以存放关键字以及与其相关的页面地址。关键字的提取将是一个手动进程,关键字的选择完全取决于执行选择的人。对索引的查询将使用标准结构化查询语言(Structured Query Language,SQL)来执行。

但是随后出现了一种不同的看法。我们手头已经有了实现此功能所需的一切:用于所有服务器的完整 Windows 环境和内部开发已经使用的 Web 开发工具 (ColdFusion)。惟一缺少的一件东西是有关如何使用此工具来实现此功能的文档。我们可以获得许多有关 Active Server Pages (ASP) 的信息,但是却缺少有关 ColdFusion 的资源。作为我们团队中惟一一个具有必要技能的成员,我捋起了袖子,构建了用于 ASP 的 ColdFusion 等价物,它成为我们的搜索工具。在这个过程中,我决定与社区一起分享。下面开始介绍给大家。

本文讲什么?
对有效且有用的搜索工具感兴趣吗?学习如何通过将 Windows Indexing Service 与 Ajax 和成熟服务器技术相结合,来开发这种工具。

介绍此工具

在我讲述如何构建搜索工具的细节之前,本节介绍贯穿于全文使用的工具。每个工具都在最后的解决方案中扮演着特定的角色:Rico JavaScript 增强显示结果时的用户体验,ColdFusion MX 7 访问索引并构建搜索结果,Windows Indexing Service 提供索引基础设施。

Rico JavaScript 库

请访问 Ajax 技术资源中心,这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。

RSS 订阅 Ajax 相关文章和教程的 RSS 提要

Rico JavaScript 库是在 Apache 2.0 Licens 下可用的开源 Asynchronous JavaScript + XML (Ajax) 框架。它着眼于简单性,即通过提供单个 JavaScript 对象来添加对任何 HTML 页面的支持。

要开始使用它,必须在超文本标记语言 (Hypertext Markup Language, HTML) 页面标题中包括提供必要功能的 JavaScript 库:

<script src="prototype.js"></script>
<script src="rico.js"></script>

这样做会导致自动创建 Ajax 引擎的一个实例,并通过一个名为 ajaxEngine 的对象使其可访问。

除了拖放支持和动画效果之外,此 Ajax 框架还提供了我在本文使用的下列关键特征:

  • 基本 Ajax 响应的标准 Extensible Markup Language (XML) 定义。
  • 将响应链接到特定 HTML 元素的功能
  • 使用响应内容自动更新目标 HTML 元素的 innerHTML 属性

ColdFusion MX 7

ColdFusion MX 7 是一个可用于开发高性能动态网站的应用服务器和 Web 开发框架。ColdFusion 的强大都来自于它的灵活语言:ColdFusion Markup Language (CFML)。此语言的语法模仿 HTML,因此很容易掌握。CFML 提供了大量的标记来封装或扩展 HTML 并执行条件处理、与本地文件系统交互、执行 HTTP 或 FTP 操作、动态生成 PDF 文件、连接到外部应用程序创建的各种数据,等等。使用所有的内置功能以及与其他应用程序或服务集成的灵活性,CFML 可以立即满足您的所有 Web 开发需求。

当然,您可以使用其他语言来提供本文展示的搜索页面。您可以使用任何 Web 语言,比如 PHP、 ASP 或 Ruby on Rails 等,只要这种语言能够访问 Component Object Model (COM) 对象。这是使用 Windows Indexing Service 访问索引内容时的惟一需求。

Windows Indexing Service

Windows Indexing Service 是安装在 Windows XP 或 Windows Server 2003 系统上的基础组件。它的角色是分析文件系统上或 Web 服务器中的文档属性和内容,然后构建一个索引目录以方便对此数据的搜索。

可以在 Add/Remove Programs 工具(可以通过 Control Panel 直接访问)的 Add/Remove Windows Components 区段中检查其安装(参见图 1):


图 1. 添加/删除 Windows 组件
添加/删除 Windows 组件

一旦安装完成,在 Services and Applications 区段中,可以通过 Windows Administrative Tools 中的 Computer Management 控制台访问并管理此组件(参见图 2):


图 2. Computer Management 控制台
Computer Management 控制台

此工具是系统的一部分并完全集成,因此当您在完全 Windows 环境中工作时,此工具是首选。

如果您需要跨平台的支持,可以在开源许可下使用一些替代方案。例如 Apache Lucene(高性能、功能完全的文本搜索引擎库)和 Apache Solr(基于 Lucene 搜索库的搜索服务器)。





回页首


索引 IIS 服务器的内容

在开始编码搜索工具之前,需要在 Web 服务器上激活索引,如果有必要,在目录的帮助下将其范围限制为文件子集。

激活索引

默认情况下,索引系统是活动的,并自动创建两个目录:

  • System:通过此目录来索引计算机上的所有文档。也可以在 Windows 标准搜索工具的帮助下查询此目录。
  • Web:通过此目录来索引 Web 服务器上的所有文档;所有文档存储在 c:\inetpub\wwwroot 目录下。

在搜索 Web 服务器上的内容时,如果没有指定任何内容,则默认使用 Web 目录。大多数时候,这足够了。然而,您可能想创建附加目录,例如,如果想在您的站点上提供几个搜索页面,每个都限制到站点的一个子集中,或者如果几个网站都位于同一个 Microsoft Internet Information Services (IIS) 服务器上。

添加新的目录

在 Computer Management 控制台的 Indexing Service 子区段中,通过在上下文菜单中选择 New > Catalog 来创建新目录。然后指定新目录的名称以及在磁盘上的位置(参见图 3):


图 3. 添加新目录
添加新目录

名为 catalog.wci 的隐藏文件夹在指定位置自动创建,并在索引期间存放所生成的文件。在此操作完成后,指定要在索引中索引或排除的文件夹,并将目录与 Web 服务器相关联(在目录属性的 Tracking 选项卡上),以便可以正确生成虚拟路径(vpath)(参见图 4):


图 4. 目录属性
目录属性




回页首


访问索引内容

因为本文讲述如何构建搜索页面,所以在客户端需要有一个页面(参见 清单 1)来充当搜索工具的用户界面。此页面呈现给用户一个基本表单,以便他们输入搜索条件。在表单下面是一个空白区域(div),稍后将显示搜索结果(参见图 5):


图 5. 搜索表单
搜索表单

清单 1. 搜索工具的用户界面 (ajax_search.cfm)
                
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Search</title>
<script src="prototype.js"></script>
<script src="rico.js"></script>

<script language="JavaScript">
var CurrentPage = 1;

function onLoad() {
  ajaxEngine.registerRequest('getSearchResults','ajax_results.cfm');
  ajaxEngine.registerAjaxElement('SearchResults');
}

function getResults(RequestedPage) {
  var SearchString = document.SearchForm.qu.value;
  CurrentPage = RequestedPage;
  ajaxEngine.sendRequest('getSearchResults',
                         "qu=" + SearchString,
                         "pg=" + RequestedPage);
}

function getPreviousResults() {
  getResults(CurrentPage-1);
}

function getNextResults() {
  getResults(CurrentPage+1);
}
</script>

</head>

<body onLoad="onLoad()">
<h1>Search</h1>

<cfform name="SearchForm" method="post" action="#CGI.SCRIPT_NAME#" 
        onSubmit="getResults(1); return false;">
  <cfinput type="text" name="qu" size="50" maxlength="100" value="">
  <cfinput type="submit" name="" value="Search">
</cfform>

<div id="SearchResults">
</div>
</body>
</html>

第一次加载此页面时 (onLoad event),请求处理程序使用 Ajax 引擎进行注册。此处理程序将服务器端页面(此页面处理此请求)的 URL 与在客户端用作其标识符的一个名称相关联。此外,因为此页面提供了一个由 Ajax 引擎使用搜索结果进行更新的 div 区段,所以此页面必须注册为一个元素:

ajaxEngine.registerRequest('getSearchResults','ajax_results.cfm');
ajaxEngine.registerAjaxElement('SearchResults');

要避免信息超载,显示结果被限制为预定义的条目数;并提供了 JavaScript 函数以进行向前或向后导航。其中每个函数都生成一个 Ajax 请求来调用服务端的页面,并将其传递给 URL 上的两个参数: qu(要查找的字符串)和 pg(要显示结果的页面数)。

在服务器端,此页面(参见 清单 2)检索 URL 参数,查询索引内容,按客户机请求选择结果的正确子集,并将典型的 ajax-response 以 XML 格式打包返回给浏览器。在 ColdFusio 中,我使用 cfcontent 标记来指定要返回给浏览器的内容类型。


清单 2. ajax_results.cfm 服务器页面
                
<cfcontent type="text/xml">
<ajax-response>
<response type="element" id="SearchResults">

<h1>Search results</h1>

<cfscript>
PageSize = 10;
MaxRecords = 50;

if (IsDefined("URL.qu")) {
  SearchString = URL.qu;
}
else {
  SearchString = "";
}

if (IsDefined("URL.pg") and IsNumeric(URL.pg)) {
  RequestedPage = Val(URL.pg);
}
else {
  RequestedPage = 1;
}
</cfscript>
<cfif SearchString eq "">
  <p>Your search did not match any documents.</p>
<cfelse>
  <cfscript>
  QueryString = SearchString & " AND ##filename *.htm?";

  ixQuery = CreateObject("COM","ixsso.Query");
  ixQuery.Query = QueryString;
  ixQuery.Columns = "filename,size,rank,characterization,vpath,DocTitle,DocAuthor";
  ixQuery.SortBy = "rank[d], DocTitle";
  ixQuery.MaxRecords = MaxRecords;

  RS = ixQuery.CreateRecordSet("nonsequential");
 
  if (RS.EOF) {
    WriteOutput("<p>Your search did not match any documents.</p>");
  }
  else {
    RS.PageSize = PageSize;

    PageCount = RS.PageCount;

    if ((RequestedPage lt 1) or (RequestedPage gt PageCount)) {
      RequestedPage = 1;
    }

    RS.AbsolutePage = RequestedPage;

    if (RS.RecordCount gt 0) {
      FirstRecordOnPage = RS.AbsolutePosition;
      LastRecordOnPage = FirstRecordOnPage + PageSize - 1;
      if (LastRecordOnPage gt RS.RecordCount) {
        LastRecordOnPage = RS.RecordCount;
      }
      WriteOutput("<p>");
      WriteOutput("Results <strong>" & FirstRecordOnPage & " - " & LastRecordOnPage);
      WriteOutput("</strong> of <strong>" & RS.RecordCount & "</strong> (page ");
      WriteOutput(RequestedPage & " of " & PageCount & ")");

      if (RequestedPage gt 1) {
        WriteOutput(" <a href='javascript:getPreviousResults()'>Previous results</a>");
      }
      if ((PageCount gt 1) and (RequestedPage neq PageCount)) {
        WriteOutput(" <a href='javascript:getNextResults()'>Next results</a>");
      }

      WriteOutput("</p>");
    }

    while (not (RS.EOF or (RS.AbsolutePage neq RequestedPage))) {
      WriteOutput("<p>");
      WriteOutput("<a href='" & RS.Fields.Item("vpath").Value & "' target='_blank'>");
      WriteOutput(XmlFormat(RS.Fields.Item("DocTitle").Value) & "</a>");
      WriteOutput("<br />");
      WriteOutput(XmlFormat(RS.Fields.Item("characterization").Value));
      WriteOutput("</p>");
      RS.MoveNext();
    }
  }
 
  RS.Close();
  ReleaseComObject(RS);
  ReleaseComObject(ixQuery);
  </cfscript>
</cfif>

</response>
</ajax-response>

查询目录

可以使用 Indexing Service 查询语言或使用 SQL 查询 Windows Indexing Service 目录。其中每个语言都支持一些 Application Programming Interfaces (API)。我在本文使用的技术基于 Query Helper,这是一种高级的 API,为访问 Windows Indexing Service 数据提供了一种面向对象的接口。此接口允许构建查询并将其提交,同时生成 ActiveX Data Object (ADO) Recordset 作为结果。

使用 ColdFusion,按照下列步骤执行上述操作:

<cfscript>
QueryString = SearchString & " AND ##filename *.htm?";

ixQuery = CreateObject("COM","ixsso.Query");
ixQuery.Query = QueryString;
ixQuery.Columns = "filename,size,rank,characterization,vpath,DocTitle,DocAuthor";
ixQuery.SortBy = "rank[d], DocTitle";
ixQuery.MaxRecords = MaxRecords;

RS = ixQuery.CreateRecordSet("nonsequential");
</cfscript>

此处使用的此对象的最重要的参数如下:

  • Query:要提交给 Windows Indexing Service 的请求,也称为限制。它是词与参数的组合,用于确定返回作为一部分搜索结果的文档。此请求可以使用不同的方言表达:Dialect 1、Dialect 2 或 SQL。在最简单的形式中,它仅包括要查找的词。要细化搜索,可以添加参数以限制结果的范围。例如,#filename *.htm 仅搜索 HTML 文件,#vpath \docs* 仅考虑 docs 文件夹。可以使用布尔操作符 (AND, OR, NOT) 组合这些表达式 (AND, OR, NOT)。
  • Columns: 要在搜索结果中返回的字段列表。其中包括下列字段:
    • filename:文档名称。
    • rank:基于搜索词的频率指定结果中文档的值。
    • characterization:文档摘要。
    • vpath: 访问文档的虚拟路径。
    • DocTitle:文档标题。
  • SortBy:基于指定的列,搜索结果的排列顺序。 [d] 选项指明其中一个字段必须按降序排列。
  • MaxRecords:从索引内容中检索的最大记录数。
  • Dialect:Indexing Service 查询语言的方言。文字值 “1” 指示方言 1,“2” 指示方言 2(默认)。
  • Catalog:用于限制搜索的一个或多个目录的名称(用逗号隔开)。目录名称是与本地计算机上隐藏目录相关联的名称。它使用类似 URL 的语法: query://hostname/indexname. The hostname 是目录所在计算机的名称;indexname 是此计算机上的目录名称。如果没有为此属性提供任何名称,则使用此计算机上的默认目录(在此情况下使用 Web,因为安装了 IIS)。

执行请求并使用 CreateRecordset 方法生成 ADO Recordset 对象。然后,使用此记录集迭代遍历搜索结果以显示给定的子集。

构建 Ajax 响应

通常,Rico Ajax 响应以格式良好的 XML 格式表示,由 <ajax-response></ajax-response> 标记来定界。响应内容也由 <response></response> 标记来定界;参见清单 3:


清单 3. 示例 Ajax 响应
                
<ajax-response>
  <response type="element" id="SearchResults">
<h1>Search results</h1>
<p>Results <strong>1 - 10</strong> of <strong>50</strong> 
(page 1 of 5) <a href='javascript:getNextResults()'>Next results</a></p>
<p><a href='/cfdocs/htmldocs/introa.htm' target='_blank'></a><br />
Getting Started Building Blackstone Applications is intended for anyone 
who needs to begin programming in the Macromedia Blackstone development environment.</p>
<p><a href='/cfdocs/htmldocs/introb.htm' target='_blank'></a><br />
CFML Reference is your primary ColdFusion Markup Language (CFML) reference.
Use this book to learn about CFML tags and functions, ColdFusion expressions, and using 
JavaScript objects for WDDX in Macromedia ColdFusion MX.</p>
  </response>
</ajax-response>

一个响应存放两个属性:typeid。这些参数指定此响应旨在更新由上述 id 标识的 (type="element") 页面上的 HTML 元素的内容。此响应的内容必须代表有效的 XHTML 代码。在示例中,在搜索页面上要更新的元素是与 id 属性(与 Ajax 属性中的相同)相关联的 div 区段。

显示搜索结果

搜索结果以 ADO Recordset 格式返回。您必须做的事情是循环遍历此对象以访问并显示其结果(参见图 6):


图 6. 显示的搜索结果
显示的搜索结果

搜索结果作为一个列表显示,其中包括作为超链接的文档名称 (DocTitle) 和文档摘要 (characterization)。

为了确保目录中的数据以有效的 XML 格式传送,可以使用 ColdFusion 函数 XmlFormat。它的角色是对字符串中的特殊 XML 字符进行转义,以便字符串可以用作 XML 文档中的简单文本。

翻页查看搜索结果

在搜索页面层(在客户机上),当前显示结果的页面数目保存在内存中 (CurrentPage)。两个 JavaScript 函数启动对前一页面 (getPreviousResults) 和下一页面 (getNextResults) 的请求。

在服务器端,使用记录集的 PageSize 属性将搜索结果分割成区块,以限制在浏览器中同时显示的结果数。设置此属性之后,可以检索记录集中可用的页面数 (PageCount)。这样,就可以将必要的导航链接添加到返回给浏览器的 XHTML 代码中。

正如在清单 2 中所看到的,还可以在 Recordset 的 AbsolutePage 属性的帮助下指定要显示的搜索结果子集(页面)。赋予此属性的值被传递到 URL 上的页面。





回页首


结束语

Windows Indexing Service 提供了一种开箱即用的向网站或 Intranet 中添加搜索功能的简单方法。通过将此方法与 Ajax 和成熟的服务器技术相结合,可以将此功能转化成一种有效的用户友好的搜索工具。






回页首


下载

描述名字大小下载方法
本文使用的 ColdFusion 脚本wa-aj-rico.zip2KBHTTP
关于下载方法的信息


参考资料

学习
  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • 用 Rico LiveGrid 小部件创建数据集导航(developerWorks,2007 年 2 月):介绍了如何使用 Rico LiveGrid 小部件轻松地在 Web 应用程序中添加 Ajax 风格的导航。

  • 阅读 Windows Indexing Service,这是 Windows 用于索引 IIS 服务器内容的一种基础服务。

  • Rico JavaScript 库是开源 Ajax 框架。

  • Adobe ColdFusion 是一个应用服务器和开发框架。

  • Ruby on Rails 是经过优化的具有可持续生产率的开源 Web 框架。

  • 学习 PHP,这是一种常用的适用于 Web 开发的脚本语言。

  • Apache Lucene 是一种开源、高性能、功能完全的文本搜索引擎库。

  • 学习 Apache Solr,这是一种基于 Lucene 搜索库的 开源服务器。

  • developerWorks Ajax 资源中心 提供了让您立即开始开发灵活的 Ajax 程序所需的工具、代码和信息。

  • 随着 Web 2.0 成为开发圈的一个热点,在 Web 开发专区 中您将找到不断增长的资源集合。

获得产品和技术
  • 下载 IBM 产品评估版,并从 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 获得应用程序开发工具和中间件产品。


讨论


关于作者

Philippe Randour

Philippe Randour 是一位有十年以上工作经验的软件和系统工程师。您可以通过 philippe@randour.net 与 Philippe 联系。




对本文的评价










回页首


DB2、IBM、Lotus、Rational、Tivoli 和 WebSphere 是 IBM Corporation 在美国和/或其他国家的商标。 Microsoft 和 Windows 是 Microsoft Corporation 在美国和/或其他国家的商标。 其他公司、产品或服务的名称可能是其他公司的商标或服务标志。

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款