XML - 数据交换管理/XQL
上一章 | 下一章 |
← XML 加密 | XQuery → |
- 什么是 XQL?
- 什么是 XQL 查询?
- 教程
- XQL 的不同组成部分是什么?
随着越来越多的信息存储在 XML 中、以 XML 交换或通过各种接口以 XML 形式呈现,智能查询我们的 XML 数据源的能力变得越来越重要。XML 文档是结构化文档——它们模糊了数据和文档之间的界限,允许将文档视为数据源,并将传统数据源视为文档。
XQL 是一种专门为 XML 设计的查询语言。与 SQL 是关系表查询语言和 OQL 是面向对象数据库中存储的对象的查询语言相同,XQL 是 XML 文档的查询语言。XQL 的基本结构直接对应于 XML 的基本结构,并且 XQL 与 XPath 密切相关,XPath 是 XSL 和 XPointers 使用的通用定位器语法。由于查询、转换模式和链接都基于可能 XML 文档中找到的结构模式,因此这三种应用程序中使用的模式语言的通用模型既是可能的,也是可取的,并且表达该模型表达的模式的通用语法简化了必须掌握各种 XML 相关技术的用户的任务。虽然 XQL 出现早于 XSL 模式,但两种语言之间存在很强的相似性,我们已将 XPath 语法用于不同的结构。XPath 中并非所有结构都需要用于查询,并且 XQL 中使用的一些结构在 XPath 中找不到,但两种语言共享一个共同的子集。
本章描述的 XQL 语言包含几个以前发布的语言版本中没有的功能,包括联接、链接、文本包含和可扩展函数。这些新功能在很大程度上受到 W3C QL '98 研讨会讨论的启发,并使人们能够以强大的方式组合来自异构数据源的信息。我们非常小心地保持了 XQL 的基本简单性,同时添加了这些功能。
本章旨在作为即将到来的 W3C 查询语言活动的输入,以及 XPath 的进一步发展。
传统上,结构化查询主要用于关系型数据库或面向对象的数据库,而文档则使用相对非结构化的全文查询进行查询。虽然相当复杂的结构化文档查询引擎已经存在了一段时间,但它们还没有成为主流应用。在过去的一年中,人们提出了许多非常不同的 XML 查询方法,对构成查询的不同视角。来自半结构化数据库社区的一些特别有趣的提案包括 XML-QL 和 Lorel,并采用半结构化方法来处理 XML。本提案将这些语言中的几个想法整合到 XQL 中。
XQL 被设计用于许多不同的 XML 环境,使用一种可以在 XML 属性中使用、嵌入编程语言或并入 URI 的语法。从一开始,我们就努力使语言保持简单和精炼,并且我们一直小心地不要添加会使 XQL 难以实现的功能。在过去的一年中,我们被说服添加了几个强大的新功能,这些功能允许用户组合来自多个来源的信息,使用链接中表达的关系作为查询的一部分,并根据文本包含进行搜索。能够使用多个文档中的信息的查询允许这些文档中包含的信息以创建原始文档的人员没有预见到的方式被重用。当许多文档或数据源可能各自包含给定主题所需的部分信息时,这非常有用。例如,假设一个文档包含针对特定学习课程的一组推荐书籍,另一个文档列出了商店的书籍和价格,第三个文档包含一组书籍评论。可以构建一个查询来列出推荐的书籍、它们的价格以及它们收到的评论。
XQL 与 XPath 密切相关,我们希望能够在 XPath 发展过程中保持与 XPath 的兼容性。我们认为 XQL 是 XSLT 的补充,XSLT 可用于对查询结果进行复杂的重塑和格式化。
XQL 设计的一个重要动机是认识到 XML 有其自己的隐含数据模型,既不是传统关系型数据库的数据模型,也不是面向对象或面向对象关系型数据库的数据模型。在 XQL 中,文档是有序的、标记的树,节点代表文档实体、元素、属性、处理指令和注释。该模型与 XML 信息集 (http://www.w3.org/XML/Group/1999/04/WD-xml-infoset-19990428.html) 兼容。
重要的是要注意,数据之间的关系包含文档中很大一部分的信息,这是 XML 等结构化文档格式首先有用的原因之一。XQL 的最初公式完全基于 XML 文档的树状结构
-
层次结构
-
父/子
-
祖先/后代
-
-
序列(在同级列表中或在文档顺序中)
-
位置(在同级列表中或在文档顺序中)
-
绝对
-
相对
-
范围
-
这些关系长期以来一直是 XPointer 模型的基础,现在通过轴的形式反映在 XPath 中。在 XQL 中,所有查询都使用子轴,因此我们将以父/子关系和祖先/后代关系的形式进行讨论,而不是使用 XPath 工作草案中的定位器路径一词。
当前草案将此模型扩展以支持以下内容
-
通过联接建立的临时关系
-
链接的取消引用
联接允许在查询中组合文档的子树;链接允许查询支持引用以及树结构。
在 XQL 中,查询从一个或多个 XML 文档中返回 XML 文档节点。为了检查 XQL 查询的特征,考虑查询发生的环境的四个基本问题是有用的
- 什么是数据库?
- 查询语言是什么?
- 查询的输入是什么?
- 查询的结果是什么?
下表提供了对每个问题的简要回答,包括与 SQL 查询语言的比较,SQL 查询语言广泛用于查询关系数据库
SQL | XQL |
数据库是一组表。 | 数据库是一组一个或多个 XML 文档。 |
查询在 SQL 中完成,SQL 是一种查询语言,它使用表的结构作为基本模型。 | 查询在 XQL 中完成,XQL 是一种查询语言,它使用 XML 文档的结构作为基本模型。 |
FROM 子句确定查询检查的表。 | 查询会得到来自一个或多个文档的输入节点列表。 |
查询的结果是一个包含一组行的表;此表可以作为进一步查询的基础。 | 查询的结果是一个 XML 文档节点列表,它可以作为进一步查询的基础。 |
从上表可以看出,文档节点在 XQL 查询中起着核心作用。这些节点是一种抽象。任何实际的 XQL 实现都会找到一些具体的实现查询中使用的节点的方法。例如,XQL 引擎可以通过 DOM 节点、XSL 节点、索引结构或 XML 文本表示查询的输入。这些方法中的任何一种都可以用来表示查询的结果;此外,还可以使用指向原始文档的超链接或其他引用,创建新的虚拟文档,或使用 DOM 二级 TreeWalkers 或 Iterators。
构成查询输入的节点可能来自各种不同的来源。它们可能是先前查询的结果,文档库的内容,来自文档对象模型节点列表的节点,或任何其他标识一个或多个文档中节点的来源。XQL 不会指定如何将这些节点带到查询中。当前的 XQL 实现采用了多种方法,包括:使用文档对象模型子树作为查询的基础,查询作为 UNIX 风格管道输入提供的整个文档,从命令行读取文档,使用数据字典或存储库目录结构来标识要查询的节点,以及使用 URL 标识文档。此提案增加了对使用连接合并来自异构数据源的信息的支持。
在 XQL 中,节点具有标识,并且它们在查询结果中保留其标识、包含关系和顺序。分组运算符允许省略树的级别,同时保留查询返回的节点的相对顺序和包含关系。连接允许将来自一个数据源的子树插入到另一个文档子树中,前提是满足连接条件。链接函数类似于连接,允许将文档中的超文本链接替换为它引用的节点。XQL 中的一些函数返回可能为布尔值、整数或字符串的值。这些值在查询模型中也被视为节点。
XQL 教程
[edit | edit source]在深入研究之前,我们认为展示一些典型的 XQL 查询将有助于传达对该语言的直观感受。本教程将讨论最简单的 XQL 查询,这些查询也可能是最常见的。在本教程中,我们将对 XQL 进行快速概述,而不花时间进行精确描述。
一个简单的字符串被解释为一个元素名称。例如,此查询规范返回所有 <table> 元素
table
子运算符(“/”)表示层次结构。此查询规范返回 <front> 元素的子元素 <author> 元素
front/author
文档的根可以使用前导 “/” 运算符表示
/novel/front/author
编者注: 在 XQL 中,文档的根指的是文档实体,在技术上,XML 意义上的文档实体,它基本上等同于文档本身。它与根元素不同,根元素是包含文档中其余元素的元素。文档根始终包含根元素,但它也可能包含文档类型、处理指令和注释。在这个例子中,<novel> 将是根元素。
路径始终从上到下描述,除非另有说明,否则路径上最右边的元素将被返回。例如,在上面的例子中,将返回 <author> 元素。
元素的内容或属性的值可以使用等号运算符(“=”)指定。以下返回所有 <front> 元素的子元素,其名称为 “Theodore Seuss Geisel” 的作者
front/author='Theodore Seuss Geisel'
属性名称以 “@” 开头。它们被视为它们所属元素的子元素
front/author/address/@type='email'
后代运算符(“//”)表示任意数量的中间级别。以下显示了 <front> 中的任何位置的地址
front//address
当后代运算符出现在路径的开头时,它表示从文档开始的所有节点。此查询将查找文档中的任何地址
//address
过滤器运算符(“[ ]”)根据括号内的条件过滤其左侧的节点集。以下查询返回地址;这些地址中的每一个必须有一个名为 “type” 的属性,其值为 “email”
front/author/address[@type='email']
请注意,“address[@type='email']” 返回地址,而 “address/@type='email'” 返回 type 属性。
可以使用布尔运算符组合多个条件。
front/author='Theodore Seuss Geisel'[@gender='male' and @shoesize='9EEEE']
括号也用于下标,它表示文档中的位置。以下指的是第 1、3、4、5 和 8 节,以及最后一节
section[1,3 to 5, 8, -1]
条件和下标不能同时出现在同一个括号中,但括号的两种用法可以出现在同一个查询中。以下指的是前三个 level 属性值为 “3” 的节;换句话说,它返回前三个 “level3” 节
section[@level='3'][1 to 2]
现在我们已经了解了基础知识,让我们看一下文档,并尝试对其进行一些 XQL 查询。以下是发票文档。传统上,发票通常存储在数据库中,但发票既是文档又是数据。XQL 被设计为可以处理文档和数据,前提是它们通过某种接口以 XML 的形式表示。此文档将作为以下示例查询的基础
<?xml version="1.0"?> <invoicecollection> <invoice> <customer> Wile E. Coyote, Death Valley, CA </customer> <annotation> Customer asked that we guarantee return rights if these items should fail in desert conditions. This was approved by Marty Melliore, general manager. </annotation> <entries n="2"> <entry quantity="2" total_price="134.00"> <product maker="ACME" prod_name="screwdriver" price="80.00"/> </entry> <entry quantity="1" total_price="20.00"> <product maker="ACME" prod_name="power wrench" price="20.00"/> </entry> </entries> </invoice> <invoice> <customer> Camp Mertz </customer> <entries n="2"> <entry quantity="2" total_price="32.00"> <product maker="BSA" prod_name="left-handed smoke shifter" price="16.00"/> </entry> <entry quantity="1" total_price="13.00"> <product maker="BSA" prod_name="snipe call" price="13.00"/> </entry> </entries> </invoice> </invoicecollection>
现在让我们看看一些示例查询。对于这些示例,我们将使用文本形式呈现查询结果,使用 “查询结果和序列化” 部分中描述的序列化方法。通常,XQL 查询返回节点列表,这些节点列表可以用任何方便查询执行环境的方式表示,例如 DOM 节点、序列化的 XML 文本、XPointers、超链接,或通过创建一个迭代器来遍历结果。由于 XML 文本易于阅读,我们发现它适合作为我们的示例中表示结果的一种方式。
假设我们想查看数据库中的所有客户。我们可以执行以下查询
查询
//customer
结果
<xql:result> <customer> Wile E. Coyote, Death Valley, CA </customer> <customer> Camp Mertz </customer> </xql:result>
我们可能想查看 BSA 制造的所有产品。此查询将完成此任务
查询
//product[@maker='BSA']
结果
<xql:result> <product maker="BSA" prod_name="left-handed smoke shifter" price="16.00"/> <product maker="BSA" prod_name="snipe call" price="13.00"/> </xql:result>
当在与返回结果不同的路径上指定条件时,过滤器特别有用。例如,以下查询返回 Camp Mertz 订购的产品
查询
//invoice[customer='Wile E. Coyote, Death Valley, CA']//product
结果
<xql:result> <product maker="ACME" prod_name="screwdriver" price="80.00"/> <product maker="ACME" prod_name="power wrench" price="20.00"/> </xql:result>
这是本教程的结尾,它只涵盖了 XQL 的最基本功能。有关说明更新或更高级功能(例如返回运算符、序列、连接、引用和用户定义函数)的示例,请参阅下一部分的相应部分。
XQL 表达式
[edit | edit source]XQL 查询始终针对上下文进行求值,上下文是一个文档节点列表。查询的初始上下文称为起始上下文。在 XQL 中,起始上下文中的节点可能来自不同的文档,即使它们在同一个文档中,也没有假设它们来自文档的连续部分。一些 XQL 运算符建立一个新的上下文,在该上下文中将对子表达式进行求值;例如,在表达式 “author/name” 中,在起始上下文中对 “author” 进行求值。对于每个作者,"/" 运算符建立一个新的上下文,该上下文包含该作者的子元素,并在该上下文中对 “name” 进行求值。建立新上下文的运算符是 /、// 和 []。
编者注: 在 XSL 中,表达式针对称为上下文节点的节点进行求值。我们使用 “上下文” 一词的目的是为了与 XSL 模式保持语义一致性,而不会对查询语言施加不必要的限制。因此,XSL 模式是根据上下文节点的子节点定义的,而 XQL 查询是根据上下文节点本身定义的。我们通过构建一个包含上下文中节点的虚构上下文节点,并允许 XSL 术语 "." 映射到该上下文节点,来维护 XSL 模式定义和 XQL 定义的对应关系。
术语
[edit | edit source]以下表达式是术语,它们根据节点的类型或名称从上下文中选择特定的节点
n | 元素名称 | 上下文中所有节点类型为元素且节点名称为 “n” 的节点。 |
* | 带通配符的元素名称 | 在节点类型为元素的上下文中,所有节点。 |
@n | 属性名称 | 在节点类型为属性且节点名称为“n”的上下文中,所有节点。 |
@* | 带通配符的属性名称 | 在节点类型为属性的上下文中,所有节点。 |
text() | 文本节点 | 在节点类型为文本的上下文中,所有节点。 |
comment() | 注释 | 在节点类型为注释的上下文中,所有节点。 |
pi() | 处理指令 | 在节点类型为处理指令的上下文中,所有节点。 |
pi("v") | 带有目标的处理指令 | 在节点类型为处理指令且目标为“v”的上下文中,所有节点。 |
. | 上下文节点 | 上下文节点的父节点 - 此节点可能是真实的或虚构的。 |
命名空间和名称
[edit | edit source]在 XML 表达式中,名称可以与命名空间前缀相关联。可以使用变量声明来声明命名空间前缀。在以下查询中,第一行将“b”声明为一个变量,等效于命名空间 URL "http://www.TwiceSoldTales.com"。查询的第二行搜索属于此命名空间的所有 <book> 元素。
b := "http://www.TwiceSoldTales.com"; //b:book
XML 文档可能使用不同的命名空间前缀来表示相同的命名空间 URI。匹配是根据命名空间 URI 进行的,而不是根据文档或 XQL 查询中与之关联的前缀进行的。
XQL 表达式可以明确指定在匹配节点名称时是否应考虑命名空间。
table | 任何名为 <table> 的元素,无论它属于哪个命名空间。 |
html:table | 任何名为 <table> 且属于前缀“html”指示的命名空间的元素。 |
* | 任何元素,无论它属于哪个命名空间。 |
:table | 任何名为 <table> 且未声明命名空间的元素。 |
*:table | 任何名为 <table> 且已声明命名空间的元素。 |
html:* | 任何属于与前缀“html”关联的命名空间的元素。 |
:* | 任何未声明命名空间的元素。 |
*:* | 任何已声明命名空间的元素。 |
相同的约定适用于属性名称。在属性名称中,属性前缀位于命名空间前缀之前。
@lib:isbn
命名空间在查询的输出中保留。要更改输出中节点的命名空间,请使用重命名运算符。
比较
[edit | edit source]比较根据节点的内容或值添加约束。考虑以下示例。
author="Washington Irving"
@id="id-sec-0203"
text() = "Whan that Aprille with his shoures soughte"
无论比较左侧的节点类型是什么,它都与右侧的值进行比较。对于使用支持数据类型的模式的系统,这些数据类型在比较中使用。
books[pub_date < date("1990-01-01")]
由于某些使用 XQL 的环境具有受限的字符集,例如 URI 或存储在属性值中的查询,因此许多比较具有满足这些环境的语法约束的替代语法。例如,以下两个查询是等效的。
books[pub_date < date("1990-01-01")]
books[pub_date lt date("1990-01-01")]
以下比较运算符在 XQL 中可用。
相等 | n="value" |
n eq "value" | |
不区分大小写的比较 | n ieq "value" |
不相等 | n !="value" |
n ne "value" | |
文本包含 | n contains "value" |
不区分大小写的文本包含 | n icontains "value" |
文本比较支持通配符“*”和“?”。考虑以下示例。
数据
<editor> <name> <first> Ramesh </first> <last> Lekshmynarayanan </last> </name> </editor></customer>
查询
//(editor contains "Leksh*")
值“Leksh*”匹配名称“Lekshmynarayanan”,并返回 <editor> 元素。
以下运算符可能在支持数据类型的 XQL 环境中定义。
小于 | n < value |
n lt value | |
小于或等于 | n <=value |
n lte value | |
大于 | n > value |
n gt value | |
大于或等于 | n >=value |
n gte value |
层次结构和过滤器
[edit | edit source]这些运算符建立一个新的搜索上下文,并在该上下文中评估子表达式。在本表中,Q1 和 Q2 用于表示任意 XQL 表达式。
Q1/Q2 | 父/子 | 在当前上下文中评估的满足 Q1 的节点的子节点,这样 子节点满足 Q2。Q2 对 Q1 中每个节点的子节点列表分别进行评估;每个子节点列表评估到的节点进行联合 在一起。 |
Q1//Q2 | 祖先/后代 | 在当前上下文中评估的满足 Q1 的节点的后代节点, 这样后代节点满足 Q2。Q2 对 Q1 中每个节点的每个子节点列表分别进行评估,并对每个子节点列表中的每个节点递归地进行评估; 每个子节点列表评估到的节点进行联合在一起。 |
Q1[Q2] | 过滤器 | 在当前上下文中评估的满足 Q1 的节点,包含 满足 Q2 的子节点。Q2 对 Q1 中每个节点的子节点列表分别进行评估;每个子节点列表评估到的节点进行联合在一起。 |
Q1[poslist] | 下标 | 在当前上下文中评估的满足 Q1 的节点,其在评估列表中的位置包含在 poslist 中。 |
布尔运算符和集合运算符
[edit | edit source]可以使用布尔运算符和集合运算符组合术语或其他 XQL 表达式。
not(q) | 否定 | 上下文中所有表达式 q 评估为 null 的节点。 |
q1 union q2 | 联合 | 在上下文中评估的 q1 和 q2 的联合。 |
q1 intersect q2 | 交集 | 在上下文中评估的 q1 和 q2 的交集。 |
q1 | q2 | 联合 | 在上下文中评估的 q1 和 q2 的联合。 |
q1 ~ q2 | 两者 | 如果 q1 和 q2 都非空,则返回 q1 联合 q2;如果任何一个为空,则返回空列表。 |
q1 or q2 | 或 | (布尔值) 如果在上下文中评估的 q1 和 q2 的联合非空,则返回 true;否则返回 false。 |
q1 and q2 | 和 | (布尔值) 如果在上下文中评估的 q1 和 q2 的交集非空,则返回 true;否则返回 false。 |
引入“两者”运算符是因为我们发现许多查询使用过滤器对要在过滤器之外返回的相同数据表达约束,从而导致表达式非常冗余。例如,以下查询使用过滤器来表达只有名为“Wile E. Coyote”的客户的账单包含产品才是我们感兴趣的,并且应返回客户姓名和产品集。
//invoice[customer[name='Wile E. Coyote'] and .//product]/(customer | .//product)
使用“两者”运算符,可以更简洁地表达相同的查询。
//invoice/(customer[name='Wile E. Coyote'] ~ .//product)
请注意,“两者”运算符既不是布尔“或”运算符,也不是集合交集运算符。表达式“customer intersect product”始终返回空结果,因为没有任何元素同时是 <customer> 元素和 <product> 元素。“两者”运算符用于指定必须同时满足的条件以创建上下文。
分组运算符
[edit | edit source]使用原始文档的结构对结果进行分组通常很有用。例如,列出账单上产品的查询可能希望按账单对产品进行分组,将每组产品放置在账单标签中。XQL 提供了一个分组运算符,提供了 exactly 此功能。在以下查询中,花括号左侧的元素(分组元素)用于将花括号内查询的结果进行分组。
//invoice { .//product }
对于查询匹配的每个分组元素,分组运算符都会创建一个具有相同名称的空元素。然后,花括号内查询的结果将作为子节点附加到此新节点。如果我们将此查询应用于教程中提供的账单数据,我们将获得以下结果。
<xql:result> <invoice> <product maker="ACME" prod_name="screwdriver" price="80.00"/> <product maker="ACME" prod_name="power wrench" price="20.00"/> </invoice> <invoice> <product maker="BSA" prod_name="left-handed smoke shifter" price="16.00"/> <product maker="BSA" prod_name="snipe call" price="13.00"/> </invoice> </xql:result>
使用分组运算符的复杂查询可以通过适当使用空格来提高可读性,例如。
invoice { .//customer[name contains "Coyote"] { name | address } ~ entries { .//product[@maker="ACME"] } }
序列
[edit | edit source]XQL 为序列定义了以下运算符。
之前 | a before b | 返回所有位于“b”之前的“a”。 |
之后 | a after b | 返回所有位于“b”之后的“a”。 |
列表串联 | a, b | 返回一个列表,其中包含所有“a”,然后是所有“b”。对指定返回列表中的顺序很有用。 |
列表串联运算符用于在返回列表中指定顺序。通常,XQL 运算符会维护文档顺序;串联运算符允许在返回列表中指定顺序。例如,以下查询指定返回结果的顺序应为作者、标题、然后是 isbn。
//book//(author, title, isbn)
如果有多个作者,则所有作者都将在标题之前列出。
在主要使用 XML 表示来自面向对象系统或关系数据库的数据的系统中,顺序可能并不特别重要。但是,顺序在文档中很重要,并且在数据导向的应用程序中也很有用,在这些应用程序中,标记没有清楚地指示每个元素的作用。考虑以下表格,其中列出了某项虚构运动的最新得分。
西部联赛 | |
土豚 12 | 黄鼠狼 10 |
蚊子 17 | 鼻涕虫 2 |
南部联赛 |
|
龟 25 | 野兔 0 |
鸭嘴兽 17 | 变形虫 16 |
该表的标记如下所示
<table width="50%" border="1"> <tbody> <tr> <td colspan="2"><emph>Western League</emph> </td> </tr> <tr> <td Aardvarks 12</td> <td>Weasels 10</td> </tr> <tr> <td Mosquitos 17</td> <td>Bulls 2</td> </tr> <tr> <td colspan="2"><emph>Southern League</emph></td> </tr> <tr> <td Tortoises 25</td> <td>Hares 0</td> </tr> <tr> <td Platypii 17</td> <td>Amoebae 16</td> </tr> </tbody> </table>
纯粹主义者可能会反对说这不是特别好的标记,因为它没有清楚地区分联赛和得分。我们同意,当我们编写自己的文档时,我们会用不同的方式编写它们;但是,现实世界中存在许多平庸的标记,在查询文档时,我们没有先重写它们的奢侈。因此,我们认为查询语言应该能够管理上面显示的类似数据。
要查找西部联赛的所有最新得分,我们可以使用以下查询
table//((tr after (tr contains "Western League")) before (tr contains "Southern League"))
编者注: 顺序由 XPath 中的轴处理。我们认为 XML 查询语言应该提供一些方法来允许查询中的顺序,并且应该考虑各种方法。这里讨论的方法在表达多个节点之间的关系方面具有优势,尤其是在仅在特定节点的后代内进行比较时。
函数
[edit | edit source]XQL 的大多数函数直接来自 XSL 模式语言。添加了一些函数,省略了许多函数,因为我们发现它们在纯查询环境中比在通用转换环境中更不相关。
集合函数
attribute(),attribute('name') | 返回上下文中的属性。如果提供了名称参数,则返回具有给定名称的属性。 |
comment() | 返回上下文中的注释。 |
element(),element('name') | 返回上下文中的元素。如果提供了名称参数,则返回具有给定名称的元素。 |
entity-ref() | 返回上下文中的实体引用。XQL 在所有实体引用都已扩展的文档视图上运行;此函数是 在 XQL 中定位实体引用的唯一 方法。 |
node() | 返回上下文中的所有节点。 |
pi(),pi('target') | 返回上下文中的处理指令。如果提供了目标参数,则返回具有给定目标的处理指令。 |
text() | 返回上下文中的文本节点。为了文本节点,XQL 假设 CDATA 部分被视为文本,相邻的文本节点被 合并,并且实体引用被扩展。 |
count() | |
id() | |
idref() | |
position() |
可扩展函数
[edit | edit source]许多 XQL 实现是编程环境的一部分。在这些环境中,允许用户编写自己的函数很有用,这些函数可以在查询中使用。这必须以与语言无关的方式完成,因为 XQL 实现已使用多种语言完成,包括 C++、Java、Haskell 和 Perl。为了允许编写用户定义的函数,XQL 提供了一个名为“function()”的函数。
假设用户想要添加一个计算一组值的平均值的函数。用户可以编写一个名为“average”的函数,并在 XQL 查询中调用它,如下所示
average(property//price)
用户定义的函数通常在 XQL 实现的语言环境中编写;例如,如果 XQL 实现是用 Java 编写的,用户定义的函数通常被写成 Java 函数。所有 XQL 函数都会传递当前上下文中节点的列表。如果函数有参数,则这些参数将作为字符串传递给 XQL 函数。通常,该函数将评估这些参数作为针对当前上下文的查询;例如,实现“average”函数的用户代码可能会首先执行查询“property//price”以获取当前上下文的 <price> 元素集,然后计算这些元素的平均值。
函数调用的结果也是节点列表。如果要返回单个值,例如字符串或数字,则应将其作为该类型的元素节点返回
<xql:number> 112,000.47 </xql:number>
函数可以返回的可用类型集在“查询结果和序列化”部分中描述,该部分位于当前部分之后。如果函数使用错误的参数调用,可以通过在结果中返回 <xql:warning> 元素来传达此信息
<xql:warning> "average" 需要被平均节点的数字值 </xql:warning>
编者注: 一些供应商要求也提供可扩展操作符。这将是一个有用的功能;到目前为止,我们还没有在 XQL 中找到可扩展操作符的干净设计。
问题(function-namespace): 关于命名空间在供应商和用户向 XQL 添加函数时是否会增加显著价值,存在不同的观点。
参考
[edit | edit source]编者注: 本节中的想法是探索性的,尚未包含在 XQL 中。
目前 XQL 中没有用于取消引用链接的语法,但这在许多应用程序中显然是需要的。XSL 提供了“id()”函数,它返回包含给定 ID 的元素。例如,以下内容将评估为 <A> 元素中的 HREF 属性指向的节点
A/id(@HREF)
从 XQL 的角度来看,这实际上是一种联接。但是,上面的语法比等效的联接语法更简单
A/id[$h = @HREF]/(//*[id=$h])
我们需要类似于 id() 的功能,将此功能扩展到包含任何类型的链接,而不仅仅是 ID/IDREF。让我们创建一个名为 ref() 的函数,它返回 XPointer 或 HTML HREF 指向的节点或节点
A/ref(@HREF)
联接语法的一个优点是它允许指定引用节点的类型。能够将其指定为函数的另一个参数可能很有用。让我们允许将引用节点的类型指定为函数的第二个参数。例如,以下内容将仅在引用节点是“table”元素时返回该节点;否则,它将返回 null
A/ref(@HREF, "table")
指定其他参数也可能很有用,例如将引用的范围限制为当前文档、本地存储库或其他可识别的范围。
能够识别其他节点对特定节点的引用通常很有用。例如,如果我们正在考虑从文档中删除某些内容,我们可能想知道它是否被引用。为此,引入另一个函数可能很有用,该函数返回引用特定节点的所有节点。如果我们称此函数为“backref()”,它可能看起来像这样
A/backref(table[0])
问题(ref-scope): 反向引用也需要以某种方式进行范围限定,并非所有系统都希望支持它们,因为实现开销。
引用也可以用于指定查询中使用的文档的 URL
ref("http://www.amazon.com")//book[.//title contains "Alhambra"]
联接
[edit | edit source]编者注: 联接是 XQL 中的一项新功能。本节中讨论的联接方法主要来自 GMD-IPSI 的 Peter Fankhauser 和 Software AG 的 Harald Schöning。GMD-IPSI 的 Gerald Huck 在完善初始模型方面特别有帮助。这种方法有一些初步的实现经验。
在许多环境中,能够组合来自多个来源的信息以创建一个统一的视图很有用。例如,假设我们有书籍来源和评论来源
<book> <isbn> 84-7169-020-9 </isbn> <title> Tales of the Alhambra </title> <author> Washington Irving </author> </book> <review> <isbn> 84-7169-020-9 </isbn> <title> Tales of the Alhambra </title> <reviewer> Ricardo Sanchez </reviewer> <comments> A romantic and humorous account of the time that the author of "The Legend of Sleepy Hollow" lived in an Arabian palace in Spain. </comments> </review>
我们可能想将它们组合起来创建一个包含评论中找到的评论的书籍视图
<book> <isbn> 84-7169-020-9 </isbn> <title> Tales of the Alhambra </title> <author> Washington Irving </author> <review> <reviewer> Ricardo Sanchez </reviewer> <comments> A romantic and humorous account of the time that the author of "The Legend of Sleepy Hollow" lived in an Arabian palace in Spain. </comments> </review> </book>
这相当于将信息从评论插入到书籍中。如果我们有一个数据库,它只包含这本书和这条评论,我们可以通过以下查询获得期望的结果
/book { isbn | title | author | //review { reviewer | comments } }
如果我们使用包含许多书籍和许多评论的数据库,上面的查询将在每本书中包含整个评论列表,而不仅仅是关于这本书的评论。我们需要一些方法来将评论限制在与这本书具有相同 ISBN 号码的评论。我们将通过引入相关变量来做到这一点。在以下示例中,“$i := isbn”将变量“$i”分配给每本书上下文中 isbn 的评估。表达式“//review[isbn=$i]”将评论限制在与“$i”匹配的评论
/book[$i:=isbn] { isbn | title | author | //review[isbn=$i] { reviewer | comments } }
编者注: 虽然过滤器和变量绑定都使用方括号表示法,但变量绑定不会过滤结果。例如,表达式“/book”和“/book[$i:=isbn]”将始终返回相同的一组书籍,无论是否存在任何 <isbn> 元素。
变量绑定会随着新搜索上下文的创建而传播;当创建新上下文时,例如作为子操作符或后代操作符的结果,它将继承所有活动的变量绑定。这允许在文档层次结构中声明较高的绑定用于在较低位置执行的联接。
如果一个关联变量绑定到一个求值为多个结果的子表达式,则结果列表中的任何值都将用作连接的基础。更准确地说,“list1 relop list2” 评估为“list1 中的所有 e1,只要 list2 中存在某个 e2,满足 e1 relop e2”。
以下查询返回图书,无论它们是否具有 ISBN;评论只有在具有匹配的 ISBN 时才会返回。
/book[$i:=isbn] { $i | title | author | //review[isbn=$i] { reviewer | comments } }
编辑说明: 在这个例子中,直观地说,你不能在空值上连接 - 没有 ISBN 的书不匹配所有没有 ISBN 的评论。在 XQL 邮件列表中,关于是否应该允许在空值上连接存在一些不同的意见。
在 XQL 中,方括号用于三种不同的不能混合的东西:下标、过滤器和变量绑定。如果你想要过滤器和变量绑定,你必须使用单独的方括号。
/book[isbn][$i:=isbn] { $i | title | author | //review[isbn=$i] { reviewer | comments } }
可以使用重命名操作符“->”重命名列表中的节点。在连接中,这可以用来反映一个有意义的名称,描述合成的结果。
/book[isbn][$i:=isbn] -> BookWithReviews { $i | title | author | //review[isbn=$i] { reviewer | comments } }
重命名操作符也可以用来调整查询结果中的命名空间。由于重命名会更改节点的名称,因此它也会更改命名空间。例如,假设 <book> 位于 "http://www.TwiceSoldTales.com" 的命名空间中,我们将 <book> 元素重命名为 <livre>
//book->livre
我们可以假设 <livre> 未在与 "http://www.TwiceSoldTales.com" 关联的命名空间中定义。由于重命名通常会创建原始命名空间中不存在的元素名称,因此 XQL 中的重命名不会保留原始节点名称的命名空间。重命名操作符的这个属性可以用来删除命名空间;例如,以下查询将 <book> 元素放置在默认命名空间中,无论它们的原始命名空间是什么。
//book->book
可以使用重命名操作符显式应用新的命名空间前缀。
//book->a:book
XQL 表达式从左到右计算。下表显示了 XQL 中操作符的优先级。
按优先级递减排列的查询操作符 | |
分组 | () |
过滤器 | [] |
重命名 | -> |
分组 | { } |
路径 | / // |
比较、赋值 | = != < <= > >= eq ne lt le gt ge contains ieq ine ilt ile igt ige icontains := |
交集 | intersect |
并集 | union | |
否定 | not() |
合取 | 和 |
析取 | 或 |
序列 | before after |
语句结束 | ; |
括号可以用来分组。
(author | editor)/name
author | (editor/name)
在某些环境中,查询的结果以 XML 文本形式返回。XQL 定义了一种序列化格式,允许查询结果以格式良好的 XML 文档的形式返回。命名空间用于区分属于序列化格式的标签和查询返回的标签。当查询结果被序列化时,它们会被包装在 <xql:result> 元素中。
<xql:result xmlns:xql="http://www.metalab.unc/xql/serialization"> <customer> Wile E. Coyote, Death Valley, CA </customer> <customer> Camp Mertz </customer> </xql:result>
这样做的原因是格式良好的 XML 文档可能只有一个根元素,而查询可能返回任意数量的结果。其他 XQL 序列化元素用于从函数返回的值,提供有关查询的附加信息,或指示错误或警告。以下元素在 XQL 序列化命名空间中定义。
<xql:result> | 围绕查询的序列化结果。 |
<xql:query> | 可选。包含原始查询字符串。这对调试很有用。 |
<xql:true> | 由布尔函数返回。 |
<xql:false> | 由布尔函数返回。 |
<xql:number> | 由数字函数返回。 |
<xql:text> | 由文本函数返回。 |
<xql:attribute name="attributeName" value="attributeValue"> | 用于在属性返回在元素的属性列表之外时返回属性。 |
<xql:declaration> | 用于在查询中返回 XML 声明时返回它。 |
<xql:error> | 用于指示查询中的错误。此元素的内容解释了错误。 |
<xql:warning> | 用于指示警告。此元素的内容解释了警告。 |