更新XQuery(一)

日期: 2007-12-09 来源:TechTarget中国

  XQuery有了一些新特性,内容包括从原子化到跟踪文件结构。

  在"你所不了解的XQuery"(Oracle杂志,2003年5/6月刊)一文中,我介绍了XQuery,它是一项由万维网联盟(W3C)开发的技术,设计用来查询和操纵XML数据或任何能以XML形式出现的数据,如关系型数据库。那篇引文讨论了2002年11月发布的XQuery草案规范。2003年5月,W3C发布了新的XQuery草案规范,本文追踪报道了5月份发布的草案规范中最令人感兴趣的变化和新增加的特性,其中包括库模块、序(prolog)变量、外部函数以及用于调试、错误处理和格式化的新函数。

  变化

  5月草案增加了大量新特性,但是我首先讨论对现有特性所做的更改。有些更改是表面上的。 例如,document()输入函数(该函数使用给定的统一资源标识符[URI]返回一个文档)被改为一个新的更短的名字doc()。另外,曾经被写成{- comment -}的注释现在改用新的笑脸符号(: comment :)。是的,现在每个注释都成了一个笑话。

  有些更改则是更根本的。也许最重大的改动就是distinct-values()函数不再返回节点(节点是XML结构,如元素、文档、注释以及文本节点),它只返回原子值(如整数或字符串)。尽管该函数仍然接受节点和原子值,但只返回原子值。任何进入该函数的节点都会被"原子化"并被当作原子值,然后以原子形式返回。

  原子化的规则很复杂,这里给出一些基本的:由模式定义为布尔型的元素将被原子化为true/false布尔值。定义为整型的元素将被原子化为一个整数。没有被模式定义的元素将被原子化为节点的XPath字符串值(文本节点递归地连接在一起)。

  为了说明:

  distinct-values(<item>apple</item>,
       <item>banana</item>, "grape")

  返回值("apple","banana","grape"),假设在模式中没有声明 。在下面的例子中,如果我们假设 被模式定义为布尔型,那么下面的语句:

  distinct-values(<status>0</status>,
                <status>false</status>)

  返回false(),因为它是两个元素的原子值。记住false()是XQuery常量,表示"假"。

  现在你也许会想,"当我想返回节点时,可以使用distinct-nodes()函数。" 是的,但该函数只能根据节点标识删除重复节点(那些完全相同的节点,类似于Java中引用的等效节点)。没有能删除等效节点的函数。这会使查询变得复杂,因为没有办法能轻松地删除等效节点。

  回过头来看我以前的那篇文章"你所不了解的XQuery",你将发现有些示例会受到这一改动的影响。在那篇文章中,下面的查询返回了艺术家名字的惟一列表,其中每一个名字前后都带有 标记:

  distinct-values(document("itunes.xml")
          /itunes/Tracks/Track/Artist)

  示例输出类似于:

  <Artist>Marc Cohn</Artist>
  <Artist>Pink Floyd</Artist>

  现在,执行同样的查询则返回原子值:

Marc Cohn
Pink Floyd

  由于distinct-values()去掉了 标记(这是原子化过程的一部分),所以你必须在完成distinct-values()调用后添加标记,如下所示:

  let $artists :=
  distinct-values(doc("itunes.xml")
            /itunes/Tracks/Track/Artist)
for $a in $artists
return <Artist>{ $a }</Artist>

  不是每种情况都是这么轻松地得到处理。看一下W3C 使用案例文档中的示例1.1.9.4在2002年11月版与2003年5月版中是如何变化的。该示例返回每位作者的著作列表。它使用了distinct-values(),根据2002年11月的规范,它的代码如下:

  <results>
{
  for $a in distinct-values(
    document("http://www.bn.com/bib.xml")
    //author)
  return
    <result>
    { $a }
    {
    for $b in document(
      "http://www.bn.com/bib.xml")
      /bib/book
    where some $ba in $b/author
      satisfies deep-equal($ba,$a)
 

;  return $b/title
    }
    </result>
}
</results>

  根据查询结果你不能直接分辨出姓和名,但每个 元素都由一个 和 名组成。distinct-values()调用返回具有惟一名字的 元素列表。对于2003年5月的规范,现在查询必须在姓和名上单独运行distinct-values(),而且在嵌套的FLWOR表达式中也没有将$a指定为惟一的作者:

  <results>
{
  let $a :=
    doc("http://www.bn.com/bib/bib.xml")
    //author
  for $last in distinct-values($a/last),
      $first in distinct-values(
                   $a[last=$last]/first)
  return
    <result>
    { $last, $first }
    {
    for $b in
      doc("http://www.bn.com/bib.xml")
      /bib/book
    where some $ba in $b/author
      satisfies ($ba/last = $last and
                 $ba/first=$first)
    return $b/title
    }
    </result>
}
</results>

  除了编写用户定义的distinct-deep-equal()外,没有更好的方法来完成这件事了,而该方法在纯XQuery中不能执行。(注:FLWOR(发音为"flower")表达式是XQuery的构建模块。这个名字来源于组成表达式的关键词For、Let、Where、Order by和Return}。

  新函数

  2003年5月的XQuery规范草案增加了三个新函数,它们肯定会非常有用。第一个是:

  trace($value as item()*,
     $label as xs:string) as item()*

 
  trace()函数允许在查询的中间进行printf风格的调试。该函数有两个参数:要显示的值(可以是任意多个项的序列)以及要显示的这个值的字符串标签。为方便起见,函数返回$value传递的值。trace()输出的位置由你的引擎来决定。

  该函数使你能够详细查看查询的内部过程。例如,下面的查询根据文档名返回在XQuery引擎中存储的所有文档的URI。通过增加一个trace()调用,我能够在排序前查看返回的每个URI:

  define function uris() as xs:string* {
  for $n in input()
  return trace(
    xs:string(document-uri($n)), "base:")
}
for $u in uris() order by $u return $u

  输出结果可能如下:

  2003-08-01 14:40:46 base: census.xml
  2003-08-01 14:40:46 base: ipo.xml
  
  当使用trace()和其他类似的函数时,记住在XQuery中每项内容都是一个表达式。没有语句!为了能够完成类似语句的操作,你可以采用在表达式之间加逗号的方法来创建一个序列。然后,单独计算每个表达式的值。在最终的结果序列中忽略所有返回空值的表达式。例如,下面是两个不影响结果的trace()调用:

  trace((), "starting query"),

let $time := current-dateTime()
let $ignored := trace($time, "Got time")
return
<html>
  <head></head>
  <body>Current time is { $time }</body>
</html>

  请注意第一个trace()调用后面的逗号。它使查询返回一个具有两个项的序列,第一个trace()调用的结果为空,因而被忽略。一般人不了解这一情况,但高级查询实际上就是返回一个序列,因此在这种特殊情况下两端的括号不是必需的。查询"5, "是完全正确的。此外,在这个示例中你会看到,当编写一个FLWOR表达式时,你可以执行let子句右侧的任意代码,并且忽略该值。

  5月份的草案还增加了error()函数:

  error($srcval as item()?)

  这个函数使用户能够报告一个错误,类似于抛出一个异常。$srcval被定义为item()?,意味着它可以是一个XML结构或原子,并且加上问号标记表示这是可选的。下面是一些示例应用:

  error()
error("Missing source document")
error(<span>A <i>beautifully</i>
      formatted error</span>)

  error()调用就像发生异常时那样展开堆栈。遗憾的是,XQuery仍没有try/catch功能。因此,尽管你能抛出错误,但却不能从中恢复。

  在5月份的草案中最后一个引人注目的新增函数有一个奇怪的名字:round-half-to-even()。它有两种形式:

  round-half-to-even($srcval as numeric?)
  as numeric?
round-half-to-even($srcval as numeric?,
  $precision as xs:integer) as numeric?

 
  在有一个参数的情况下,它的行为类似于round()函数,只是当一个数恰好落在其他两个数的中间时,它将参数取整为最接近的偶数值。数字理论家们会告诉你从统计学上讲这是一个更精确的取整算法。举例说明:

round-half-to-even(1.5) = 2.0
round-half-to-even(2.5) = 2.0
round-half-to-even(2.51) = 3.0

  有第二个参数的情况使函数变得很有趣。第二个参数表示精确级,并允许将函数用于格式化小数值。例如:

round-half-to-even(3.567, 2) = 3.57
round-half-to-even(1113.567, -2) = 1100.0
round-half-to-even(1 div 9, 3) = 0.111

  在声明中使用的数字数据类型是xs:decimal、xs:integer、xs:float、xs:double以及任何根据限制由它们导出的类型的一种简单表示。它用于XQuery规范中,但你不能在自己的查询中使用它。

  新的查询文件结构

  2003年5月的草案中最重要的修改包括XQuery文件结构。草案引入了主模块和库模块的概念,并利用它们添加了从可重用组件中构建查询所急需的功能。该草案还扩展了查询序(query prolog),从而提供了大量特性,如可选的版本声明、新的声明、新的导入、变量的外部定义以及函数的定义等。

  序类似于(由于边界的限制,有时字符串写在两行上):

module "http://www.w3.org/2003/05/
               xpath-functions"

default element namespace=
    "http://www.w3.org/1999/xhtml"
declare namespace xs=
    "http://www.w3.org/2001/XMLSchema"

import module
 "http://www.w3.org/2003/05/
         xpath-functions" at "logo.xq"

define function addLogo(
    $root as node()) as node()* { }

(: etc 🙂

  通过查看从5月份的规范中借用的巴科斯-诺尔范式(Backus Naur Form ,BNF),我可以描述得更精确一些:

Module        ::= MainModule |
                    LibraryModule
MainModule    ::= Prolog QueryBody
LibraryModule ::= ModuleDecl Prolog
ModuleDecl    ::= "module" StringLiteral
Prolog        ::= Version? (NamespaceDecl
                 | XMLSpaceDecl
                | DefaultNamespaceDecl
                | DefaultCollationDecl
                | SchemaImport
                | ModuleImport
                | VarDefn
              &n

bsp; | ValidationDecl)*
                FunctionDefn*
QueryBody     ::= Expr

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐

  • 更新XQuery(三)

    XQuery有了一些新特性,内容包括从原子化到跟踪文件结构。

  • 更新XQuery(二)

    XQuery有了一些新特性,内容包括从原子化到跟踪文件结构。