2003年8月对本文的修改加入了 XQUery 规范的最新更改:增加了一个新的 XQuery Serialization 和两个 全文相关的(Full-Text-related)规范,其他10个工作草案中有8个包含了新的内容,还有两个处于最后请求(Last Call)状态。实现者列表也有大幅度的增加。
W3C 的 XQuery 规范已经准备了很长时间。它启动于 1998 年由 W3C 发起的查询语言专题讨论会。来自业界、学术界和研究团体的受邀代表利用这个机会发表了各自的看法,阐述了他们认为重要的 XML 查询语言的特性和需求。
两大不同的阵营
可以在线获取这 66篇演讲稿,它们主要来自两大不同的阵营:那些将 XML 主要 作为文档使用的人(这很大程度上反映了 XML 起源于 SGML)和将 XML 作为数据使用的人 -- 后者很大程度上反映了 XML 在中间件领域、前端传统关系数据库领域中不断增长的现状。
尤其是俄勒冈州研究生院的 David Maier的演讲稿“Database Desiderata for an XML Query Language”,它非常简明扼要,最初的时候有人多次向我提到过它,说它是特别有助于了解波士顿讨论会之后成立的Query Language Working Group(查询语言工作组)的文档之一。
按照 W3C 的标准,这个工作组很庞大(据说只有Protocol Working Group(协议工作组)的成员比它多)。它由 30 多个成员公司构成,反映了这两大阵营各自的观点。现在合并而成的最终形式是一个 XML 查询语言标准, 它很好地代表了这两个团体的需求和观点。
对于 XML 用户来说,最熟悉的 XQuery 关键组件是 XPath,它本身就是一个 W3C 规范。单独的 XPath 位置路径本身 (“//book/editor” 意味着“在当前集合中查找所有图书编辑”)就是完全有效的 XQuery。在数据方面,XQuery 具有类似于 SQL 的外观和能力,这是来自关系数据库世界的用户所欢迎和熟悉的。
其卑微的出身
XQuery 是从 Quilt 开始的。最初作为用户级语法的试验器,Quilt 是基于整个工作组的协作成果的,用于定义需求、用例、底层数据模型和代数(参见下面)。Quilt 最初由 Jonathan Robie、Don Chamberlin 和 Daniela Florescu 开发。Robie 最出名的可能是他在 XQL 标准化和推广方面所做的先驱工作,XQL 是 XQuery 的前身,也是目前 XML 查询语言世界中最接近实际标准语言的一种语言,已经有十几种实现在使用。Chamberlin 是 SQL 的共同设计者之一(这可是简历中值得称道的一项!),也是“开发了大量当今关系数据库技术”的 IBM 研究小组中的一员(摘自于他的 Web 站点)。Florescu 也不逊色。她的 Web 站点描述道,她在近 10 年之内,撰写或与人合作撰写了 50 多篇关于查询语言优化和体系结构拓展的研究论文。
这三位作者提到了影响 Quilt 设计的几种语言,包括 XQL、XML-QL 和 SQL。“XML Query Language: Experiences and Exemplars(XML查询语言:经验和实例)”是一篇非常有用的论文,对于前两种语言以及叫做 YaTL 和 Lorel 的其他两种语言进行了很好的比较性概述,该论文由 Mary Fernandez、Jerome Simeon 和 Phil Wadler 合著 -- 他们都是工作组成员。
考虑到数据和文档团体的不同观点以及所打下的坚实基础(下面有详细描述),为这个庞大的规范花费如此长的时间才向大众公布也就不足为奇了。W3C 工作组的内部进展是严格保密的,并且在2001年2月中旬以前,查询语言工作组的大多数工作都是秘密进行的。
很早就发布了 Requirements 文档和 Data Model 工作草案,但是直到2001年2月,工作组的发布工作才得以进入高潮,那时大量的文档开始推出。然后在2001年6月和12月、2002年8月和11月、和2003年5月进行了重要更新。在2003年5月加入了一个 XQuery 序列化和两个全文相关的工作草案后,现在我们有了总共12个文档(包括与 XSL 工作组共同分享的6个工作草案) ,它正在(我们希望!)接近成为完整的文档集。
新生的发布王国
定义 XQuery 的、具有完全描述能力的文档集现在包括:
- XML Query Requirements
- 工作组的规划文档。XQuery 需求列表。
- XML Query Use Cases
- 解决特定问题的几个实际方案和 XQuery 代码片段。
- XQuery 1.0: An XML Query Language
- 核心文档,介绍语言本身,以及对大多数其他内容的概述。
- XQuery 1.0 and XPath 2.0 Data Model
- XML 信息集的扩展。描述查询实现必须理解的数据项和形式语义的基础。
- XQuery 1.0 and XPath 2.0 Formal Semantics
- 从形式上定义语言的底层代数。
- XML Syntax for XQuery 1.0 (XQueryX)
- 为喜欢使用 XML 的人提供的另一种语法。任何地方的机器都可以使用(至少现在是这样,除非设计出更好的替代者)。
- XQuery 1.0 and XPath 2.0 Functions and Operators Version 1.0
- Schema 数据类型、 XQuery 节点和节点序列的基本函数和操作符。
- XML Path Language (XPath) 2.0
- 单独分离出来的XPath 文档。
- XPath Requirements Version 2.0
- XPath 的需求文档。
- XSLT 2.0 and XQuery 1.0 Serialization
- 从 XQuery 1.0 和 XPath 2.0 Data Model 输出的序列化“尖括号”XML。
- XML Query and XPath Full-Text Requirements
- 描述 Full-Text Recommendation 需要达到的功能需求。
- XML Query and XPath Full-Text Use Cases
- Full-Text 规范预期能够处理的实际情况。
这些文档代表了大量工作。XQuery 1.0: An XML Query Language 文档是这个文档集的关键,但是其他文档也为 XQuery 成为良好的规范和全面支持的语言作出了贡献。据我所知,这是出自 W3C 的最复杂的一套规范(虽然 XML Schema 或许可与之相比,但那又是另一回事了)。
如果您是第一次接触如此大量的文档,并且不知道从何处着手,我可以推荐两种方法。可以从核心的 XQuery 1.0 文档开始。它有一个很好的介绍性概述。另一种方法是从选择一个 Use Cases 工作草案开始。那些文档描述了可以应用 XQuery 的几个实际方案。每个用例都以特定应用领域为目标,并针对该领域的示例数据列出了大量可能的 XQuery。如果希望看看实际工作语法的具体示例,这些代码片段是十分有用的。
BabelFish,,您在哪儿?
XQuery 比 Certs 更出色:它实际上是整合了三种语言。首先是“表面(surface)”语法,它是三者之中最为人们所见的,也是用户最有可能与之发生联系的语法。其次,是另外一种基于 XML 的语法,它是一种计算机进程更易于处理的用于代替表面语言的语法。最后,还有一种形式代数语言,它十分详细地描述了 XQuery 处理器的内部工作。
底层形式方法
Data Model and Formal Semantics(数据模型和形式语义)工作草案共同为 XQuery 提供了精确的、理论上的基础支持。这两个文档详细介绍了查询 代数,这是用形式术语定义的一组精确定义,它定义了XQuery 查询期望操作的核心实体、以及各种语言操作符怎样使用那些操作数的公式。如果您不是查询引擎的实现者、有较多的经费保障或者只是喜欢使用复杂的形式系统,那么您应该不会对它感兴趣。
提供了一个让实现者能够将表面语法特性直接重新造型为底层代数的映射。正如一些厂商在 XML 商业展示会上所展示的那样,可以实现实际上与代数直接沟通的查询处理程序(虽然我认为这更多的是对概念的证明)。
这个代数还提供了详细描述如何将复杂表达式优化及转变成更简单的等价形式的规则。我所能说的是(我不是语言学家,并且形式语义文档读起来并不轻松),它们都很好。特别是大型数据库供应商会赞赏一种从头开始设计并且经过优化而高效的查询语言结构。
代数还提供了存放类型信息的位置。XQuery 是强类型的:如果数据有与其相关的 XML Schema,则处理程序可以根据该 Schema 进行验证、并为查询引擎提供文档中节点数据类型的 后模式验证信息集(Post-Schema-Validation-Infoset PSVI)信息,同时利用“XML Schema Part 2: Datatypes”中声明的类型和自定义的用户定义类型。代数还有静态和动态类型检查能力。例如,引擎可以使用 PSVI 派生的类型信息,以在编译时静态地检查查询表达式的数据类型(当分析查询的语法正确性时)。在这个周期中尽早地确定类型无效的查询,这样能够大大减少对大型数据集进行很可能是昂贵的(和无结果的)搜索的需要。XQuery 规范最新的工作包括了对与类型有关的语法的改变。
XPath 2.0 和剩余问题
XQuery 与 XPath 2.0 有同样的公共数据模型,这一事实反映在数据模型文档的有点尴尬的标题“XQuery 1.0 and XPath 2.0 Data Model”的标题上。XPath 2.0 几乎已经成熟。数据模型描述了 XML 文档中 XPath 处理程序感兴趣的核心信息,并且 XPath 的步骤操作的最终语法和语义几乎已经完成。全部规范属于Query Language工作组和 XSL 工作组共同所有,并且这两个工作组需要对 XPath 2.0 的未来达成一致。不论在政治上还是在技术上,这常常会带来挑战。不过,就算是达成一致的道路是崎岖不平的,这两个工作组看来并没有表现出有多么不协调(至少在外人眼里如此)。
仅用一例说明为什么从 XPath 1.0 到 2.0 的转换非常有意思,考虑这一情况:XPath 1.0 是基于集合的表达式语言。XPath 1.0 中的 4 种数据类型之一,节点集,也只不过就是:集合。根据定义,集合是无序的并且不包含重复成员。另一方面,XPath 2.0 是基于序列的。相比而言,XPath 2.0 中节点的序列(是否可类推称它们为 节点序列?)是有序的,并且允许重复。用行话来说,这些差异的分歧存在于许多问题中,工作组需要各自和协作找出这些问题来,以便使他们都能够与 XPath 2.0 协调一致。
什么时候才有可能解决剩余问题呢?我不知道。在 XQuery 1.0 工作草案附录中列出了将近40个看来相当重要的问题并标记为“active”(如果它们是重要的,那么工作组现在应当已经解决它们了),但是当我一年前查看时,这个数字接近150,当时其他工作草案也有各自的附加问题列表。另一个表明过程进行的迹象,Data Model and Function and Operators 工作草案现在是处于 Last Call (最后请求)阶段,这是成为 Candidate Recommendation(侯选推荐标准)的最后一个阶段。所以隧道尽头的光线确实是正在变亮。
XQueryX
XQueryX 是另一种基于 XML 的用于表面语言的语法的规范 ,它是较早加入到 XQuery 文档家族的。XQuery 的需求之一是规定多个语法 应该是可能的 -- 听起来像是工作组在避免下注 -- 如果是这样,这些语法中的一个可能必须方便人们读写,另一个应该必须可用 XML 表示。
使用基于 XML 的查询表达具有 XML 所有显著的和已知的优点:它使标准工具易于解析、生成和询问查询内容。这可能很有用,例如,如果正在进行源码级优化或转换,那么它很可能取决于方便地检查特定语法结构的查询的能力。我们知道 XML 擅长这类任务。
尽管对于 XQueryX 做了这些早期工作,但是它是自 2001 年发布以后一次也没有更新过的少数几个工作草案之一。由于 Query 工作组没有对 XQueryX 的将来做出任何正式的声明, 所以看来显然他们不会让它以当前的形式进入 Recommendation(推荐标准) 状态。当工作草案第一次发表时,对于其冗长的语法有大量疑问的声音,难道这种批评使工作组觉得应该终止它?无论如何,目前 XQueryX 的未来尚不明确。
清单 1 显示了使用 XQuery 语法的一个简单查询:
清单 1. 一个使用标准语法的简单查询
let $authors := /book/author
return
<AUTHORS>
{
$authors
}
</AUTHORS>
|
而清单 2显示了使用 XQueryX 的等价查询:
清单 2. 使用 XQueryX 格式的相同查询:
<q:query xmlns:q="http://www.w3.org/2001/06/xqueryx">
<q:flwr>
<q:letAssignment variable="$authors">
<q:step axis="CHILD">
<q:identifier/>
<q:step axis="CHILD">
<q:identifier>book</q:identifier>
<q:identifier>author</q:identifier>
</q:step>
</q:step>
</q:letAssignment>
<q:return>
<q:elementConstructor>
<q:tagName>
<q:identifier>AUTHORS</q:identifier>
</q:tagName>
<q:variable>$authors</q:variable>
</q:elementConstructor>
</q:return>
</q:flwr>
</q:query>
|
对于处理它的计算机来说,这种冗长关系没什么问题(除非我们谈论的是 确实很大的查询),但是对于人类操作员来说,要进行一些实际的调试就会有一些困难。
一个小警告:上述“/book/author”位置路径的语法包含了我的一些猜测成分,附录中的 DTD 和 schema 对于这种表达式类型的确切语法有一些不明确的地方。这是使用早期规范存在缺陷的实例。
准备就绪,然后该如何呢?
当我2001年6月第一次撰写当时 XQuery 实现的概述时,XQuery 的第一个主要版本刚刚发表,当时只有两个实现:我自己的和 Microsoft 的。这使我有机会在文章中打趣说 Bill 和我正在争夺市场。这一次,经过两年时间和后来发布的五个工作草案,开这种玩笑的时机早已远去。已经有了20多种实现和一些相关的产品和工具。
了解当前发展情况的最好地点是 XML Query 的主页。这里的列表都是非常活跃的,我预计随着人们的兴趣和推动力量的增加以及规范越来越接近于 Recommendation(推荐标准)状态,我们将会看到新的实现不断涌现。
语法:快速样板
让我们用一个实际的例子快速看一下 XQuery 的几项功能。下面是一个简单的查询,它操作 Use Cases 文档中的规范示例文件。该查询展示了 XQuery 投影(在数据集中选择与所定标准匹配的节点子集)和 转换(生成与正被查询的文档不同的输出文档)的能力。XQuery 允许您在同一查询中指定要搜寻的内容,并且指明应该采用什么样的输出格式。
下面是该查询所操作的文档的片段:
<bib>
<book year="1994">
<title>TCP/IP Illustrated</title>
<author><last>Stevens</last><first>W.</first></author>
<publisher>Addison-Wesley</publisher>
<price>65.95</price>
</book>
<book year="1992">
<title>Advanced Programming in the Unix environment</title>
<author><last>Stevens</last><first>W.</first></author>
<publisher>Addison-Wesley</publisher>
<price>65.95</price>
</book>
<book year="2000">
<title>Data on the Web</title>
<author><last>Abiteboul</last><first>Serge</first></author>
<author><last>Buneman</last><first>Peter</first></author>
<author><last>Suciu</last><first>Dan</first></author>
</book>
...
</bib>
|
下面是我们希望产生的输出文档(进行了一些美化):
<results>
<book authorCount="1">
<author>Stevens</author>
</book>
<book authorCount="1">
<author>Stevens</author>
</book>
<book authorCount="3">
<author>Abiteboul</author>
<author>Buneman</author>
<author>Suciu</author>
</book>
...
</results>
|
下面是查询本身。它的工作是扫描所查询的文档中的所有图书,生成上面所显示的结果文档:它在输出的每个新 <book>
标签中包含计算出的 authorCount
属性;并放弃原始文档中剩下的大部分信息,只保留每位作者的姓。
(请注意,我在这里使用了术语“所查询的文档”(单数)。这是一种简化。XQuery 的数据模型也有能力处理文档集。)
<results>
{
for $book in //book
let $authors := $book/author
return
<book authorCount={ count($authors) }>
{
for $author in $authors
return
<author>{ $author/last/text() }</author>
}
</book>
}
</results>
|
另外请注意,查询本身不指定所要查询的文档或数据集上下文。这是所使用的特定查询引擎确定的。
剖析查询
下面是该查询的一些有趣特性:
for/let表达式
该示例包含两个嵌套 的 for
循环和一个 let
。外部 for
迭代由扩展路径表达式 //book 所得到每一个的节点,并将每个 <book>
节点隔离到名为 $book
的变量中。 let
表达式则取得每一本书的所有 <author>
子节点,并放到名为 $authors
的变量中。 $authors
变量包含一个节点 序列, $book
和 $author
变量都包含单节点。
请务必留意,这些变量不是 赋值的而是 绑定的。其中的差别很细微但却很重要:一旦绑定了变量,它的值就不可更改。这可以防止在运行中对变量重新赋值而造成糟糕的负面影响。另一个潜在的好处是,可以(在某种程度上)在处理期间重排包含变量的行,从而允许智能引擎优化它们的查询。
for
和 let
表达式是 FLWOR
(读作 flower)表达式的子组件。 FLWOR
表达式的正式语法:
FLWORExpr ::= (ForClause | LetClause)+ WhereClause? "return" Expr
|
显示它是一个有多种变化的表达式类型,可以生成大量不同的查询实例。正如该产品所显示的那样,“ return
”关键字后面的 Expr
项本身可以被另一个 FLWOR
表达式替代,因此,可以像不断加长的 LEGO 积木那样,将 FLWR 表达式首尾相接,无限地排成一行。能够用任何其他表达式类型替换 Expr
项,使 XQuery 具有 可编辑性并给予它丰富的表达能力。XQuery 中有大量表达式类型,在调用更通用的表达式时,可以将这些表达式插入到语法中。
一般情况下,最终是一个 return
语句终止 FLWOR
序列。在上面的查询示例中,增加了一个内部 return
作为一个方便的插入点,插入要输出的每个 <book> 的元素构造器。
元素构造器
这个查询包含三个元素构造器。通过将文字尖括号 XML 直接写入查询本身的正文中,在查询过程中动态生成元素 <results>、
<book>
和 <author>
。
在需要区分文字文本内容和元素构造器中需要判断的子表达式时,使用花括号( { 和 })。例如,如果我们编写如下所示的文字表达式则不需要用花括号来分隔内部标记和外部标记。:
顺便提一句,花括号是在 2001年7月发布的表面语言语法中加入的。更早版本的语法就不需要它们。花括号是语言在成为 Recommendation(推荐标准)过程中不断改变和发展的一个好例子。
属性构造器
下面一行
<book authorCount={ count($authors) }>
|
显示了内置属性构造器的用法。count() 函数返回每本书包含的 <author>
元素数。请再次注意花括号,它在这里用来包围需要判断的表达式。该规范的最后版本可能要求用引号分隔计算出来的属性表达式,当前这两种方法更是允许的。
内置函数和操作符
count()
是内置函数的一个示例。“Functions and Operators”草案列出了14个不同组中接近250种函数和操作符,它们构造和操作各种不同的数据类型,包括数字、字符串、布尔值、日期和时间、qname、节点和序列。
在表达式中使用 text()
操作符
<author>{ $author/last/text() }</author>
|
用从封闭的<last>元素中取得的姓氏的文本来填充每个 <author>
元素的内容。如果直接使用 $author/last
,就会将封闭的标签也一并加入,在这里我们并不希望这样。