### 用 XML 优化二次检索

在搜索引擎的设计以及类似的软件功能设计中，一个必不可少的功能就是：对已有搜索结果的二次检索。如果检索的数据集是静态数据（例如存放在数据库中），通常的做法是在已有的检索条件的基础上，动态加入新的约束条件。但是重新构造数据检索的约束条件，往往需要用户同服务器再次交互，重新下载所需数据集合并输出。如果能在客户端对已经下载的数据集合进行二次检索，将极大地减轻 Web 服务器以及数据库服务器的负担。
XML 能够很大程度地满足以上需求。它将数据内容本身与数据显示格式独立开来，分别处理。这样，如果需要改变文档的显示方式，只要修改样式文件（XSL）就可以了。XML 的自我描述特性能够很好地表现许多复杂的数据关系，使得基于 XML 的应用程序可以在 XML 文件中准确高效地搜索相关的数据内容，忽略其他不相关的部分。同时，整个数据操作都是在客户端完成，大大减轻了服务器的负担。
XML 编程接口 DOM 简介
DOM（Document Object Model，文档对象模型)是一种与平台和语言无关的编程接口，它允许程序和脚本动态访问和修改文档的内容结构和类型。它定义了一系列的对象和方法对 DOM 树的节点进行各种随机操作。
● Document 对象：是 DOM 树的最顶层节点，是对整个文档进行操作的入口。
● Element 和 Attr 对象：这些节点对象都是文档某部分的映射，节点的层次恰好反映了文档的结构。
● Text 对象：作为 Element 和 Attr 对象的子节点，Text 对象表达了元素或属性的文本内容。Text 节点不再包含任何子节点。
● 集合索引：DOM 提供了多种集合索引方式，可以对节点按指定方式进行遍历。
DOM 采用以上介绍的一系列接口来描述 XML 文档的内容和结构，即利用对象将文档模型化。这种对象模型实现的基本功能包括：
● 描述文档表示和操作的接口；
● 接口的行为和属性；
● 接口之间的关系以及互操作。
DOM 对结构化的 XML 文档进行解析，文档中的元素、实体、属性等所有个体都可以用对象模型表示。整个文档的逻辑结构类似一棵树，生成的对象模型就是树的节点，每个对象同时包含了方法和属性。DOM 提供了许多查找节点的方法。其中用于搜索的方法有：
● 根据标签名称搜索元素: 利用 Document 对象中的 getElementsByTagName 方法在全文范围内查找元素；
● 使用 XSL 模式搜索节点：所有类型的节点都带有 selectNodes 方法，调用这个方法可以利用 XSL 的模式匹配策略查找节点；
● 使用集合索引搜索节点: 索引参数都是从 0 开始计数的，例如子节点 ChildNodes 对象集。
利用 DOM，开发人员可以动态地创建 XML、遍历文档、增加（删除/修改）文档内容。DOM 提供的 API 与编程语言无关，所以对一些 DOM 标准中没有明确定义的接口，不同解析器的实现方法有可能有所差别。本文的示例采用 MSXML DOM 方案，并采用 IE 5.0 作为输出显示的浏览器。
软件实现
以下用一个简单的学生成绩查询系统为例具体说明如何实现。可以先按照学生所在的班级将学生成绩查询出来，然后可以选出平均分高于设定分数（用一个文本框接收设定分数）的学生列表。
在 SQL Server 中包含一个学生成绩表，关键字段有：学生姓名(Name)、班级(Class)、单科成绩(YuWen、ShuXue、JiSuanJi、YingYu 等)、平均成绩(AverageScore)。
首先，编辑初始页面以提供按班级查询学生成绩的功能。
<form method=post action=MyRearch. asp>
输入班号: <input type=text name=ClassName>
<input type=submit value=“提交”><input type=reset value=“重填”>
</form>
MyRearch.asp 文件中，提供按平均成绩查询的二次查询功能。关键代码如下：
<%@ Language=VBScript %>
//建立数据库连接，并根据用户需求，从数据库中检索出所有符合要求的数据集
<% Set Conn=Server.CreateObject(“ADODB.connection”) %>
<% Set rs=server.CreateObject(“ADODB.recordset”) %>
<% Conn. open Application(“SQLserver
Conn”) %>
<% sqlText=“select * from ScoreTable where Class=‘” %>
<% sqlText=sqlText & request.Form(“ClassName”) & “’” %>
<% set rs=conn.Execute(sqlText) %>
<% rs.MoveFirst %>
//在 HMTL 文件中建立一个 XML 数据对象（XML 数据岛）
//并且将数据库检索的数据集写入该数据岛中
<XML ID=“StudentList”> //该数据对象的唯一标识为 StudentList
<?xml version=“1.0” ?>
<Students>
<% do while not rs.EOF %>
<Student>
<% for i=0 to rs.Fields.Count-1 %>
//以数据库表的列名为 XML 节点的节点名
<<%=rs.Fields(i).Name %>>
<%=rs.Fields(i).Value %>
</<%=rs.Fields(i).Name %>>
<% next %>
<% rs.MoveNext %>
</Student>
<% loop %>
</Students>
</XML>
//二次检索的结果集 XML 对象（XML 数据岛）
<XML ID=“QueryResult”>
<?xml version=“1.0” ?>
</XML>
<HTML>
<HEAD>
<script language=vbscript>
sub BtnRearch_onClick
//获得第二次检索的结果集对象（XML 数据岛对象）
set doc=QueryResult.XMLDocument
//获得该结果集的根节点
set oldNode=doc.DocumentElement
//删除该根节点
doc.removeChild oldNode
//重新创建一个根节点对象，名称为 Students
set rootNode=doc. createElement(“Students”)
//重新生成根节点
doc.appendChild rootNode
//获得第一次检索的结果集对象
set doc1=StudentList.XMLDocument
//获得该对象的根节点
set rtNode=doc1.documentelement
AvaScore=document.forms(0).Score.value
rearchStr= “Student[Average>” & AvaScore & “]”
//设定新的搜索条件（二次）
set SecondList=rtNode.selectNodes(rearchStr)
for each node in SecondList
//从第一次检索的结果集中复制符合搜索条件的节点
set newNode=node.cloneNode(true)
//将符合条件的节点动态添加到二次检索结果集的根节点上
rootNode.appendChild newNode
next
end sub
</script>
</HEAD>
<BODY>
//将第一次检索的结果绑定到 HMTL 文件的一个 TABLE 对象中
<table datasrc=“#StudentList”> //指定 TABLE 对象的 XML 数据源
<tr>
<% for i=0 to rs.Fields.Count-1 %>
<th><%=rs.Fields(i).Name %></th>
<% next %>
</tr>
<tr>
<% for i=0 to rs.Fields.Count-1 %>
//指定 TABLE 对象每一列的 XML 数据字段，由于 TABLE 的 TD 对象
//本身不能绑定对象，所以只能借助 DIV 等对象绑定数据
<td><div datafld=“<%=rs.Fields(i).Name %>”></div></td>
<% next %>
</tr>
</table>
<form>
输入期望的平均分：<input type=text name=Score value=80>
<input type=button name= BtnRearch value=“二次查询”>
</form>
<h3 align=center>二次检索结果：</h3>
//将二次检索结果绑定到另外一个 TABLE 对象
<table datasrc=“#QueryResult”>
//指定 XML 数据源
<tr>
<% for i=0 to rs.Fields.Count-1 %>
<th><%=rs.Fields(i).Name %></th>
<% next %>
</tr>
<tr>
<% for i=0 to rs.Fields.Count-1 %>
<td><div datafld=“<%=rs.Fields(i).Name %>”></div></td>
<% next %>
</tr>
</table>
<% rs.Close %>
<% set rs=nothing %>
<% conn.Close %>
<% set conn=nothing %>
</BODY>
</HTML>
动态生成的 XML 数据岛对象如下：
<XML ID=“StudentList”>
<?xml version=“1.0”?>
<Students>
<Student>
<Name>李锋</Name>
<Class>9703</Class>
<YuWen>86.5</YuWen>
<ShuXue>95.2</ShuXue>
<JiSuanJi>87.0</JiSuanJi>
<YingYu>78.6</YingYu>
<AverageScore>86.825</AverageScore>
</Student>
<Student>
<Name>王可</Name>
<Class>9601</Class>
<YuWen>82.7</YuWen>
<ShuXue>83.5</ShuXue>
<JiSuanJi>89.0</JiSuanJi>
<YingYu>77.3</YingYu>
<AverageScore>83.125</AverageScore>
</Student>
……
</Students>
</XML>
以上代码在数据库为 SQL Server 7.0、操作系统为 NT 4.0、Web 服务器为 IIS 4.0、浏览器为 IE 5.0 的条件下编译调试通过。
结 论
由于所有对数据集对象的操作是在客户端完成的，实际上是服务器数据对象的一份复制品，这样做的好处是避免了网络承受大量数据通信的负担。当然，这时用户在客户端做的所有操作对服务器上存储的数据毫无影响，如果要想对服务器记录也进行修改，那么就要用到客户端与服务器端的数据交互技术（XMLHTTP 对象）了，这里就不再进行讨论。