跳转到内容

XQuery/与 XQuery 比较

来自维基教科书,开放的书籍,开放的世界

你想使用 XQuery 来比较两个项目列表,并找出它们的不同之处。

我们将使用各种函数来迭代这些列表。对于每个示例,我们将对第二个列表执行一些比较。

简单迭代和测试缺失元素

[编辑 | 编辑源代码]

在这个第一个示例中,我们将使用一个简单的 for 循环来遍历线性列表中的每个项目。然后我们将检查该项目是否出现在第二个列表中的任何位置(无论顺序如何)。如果它在第二个列表中,我们将显示第一个列表中的项目。否则,我们将输出“missing”。这在你想找出本地集合是否缺少一些远程集合中的文件时很有用。

xquery version "1.0";

(: Compare of two linear lists :)

let $list1 :=
<list1>
  <item>a</item>
  <item>b</item>
  <item>c</item>
  <item>d</item>
</list1>


let $list2 :=
<list1>
  <item>a</item>
  <item>c</item>
  <item>e</item>
</list1>

return
<missing>{
 for $item1 in $list1/item
 let $item-text := $item1/text()
 return
  <test item="{$item-text}">
  {if ($list2/item/text()=$item-text)
    then ($item1)
    else <missing>{$item-text}</missing>
  }
   </test>
}</missing>

请注意,条件表达式

if ($list2/item/text() = $item-text)

测试 $item-text 是否出现在 list2 中的任何位置。如果它出现在任何位置,此表达式将返回 true()。

示例结果

[编辑 | 编辑源代码]
<missing>
  <test item="a">
    <item>a</item>
  </test>
  <test item="b">
    <missing>b</missing>
  </test>
  <test item="c">
    <item>c</item>
  </test>
  <test item="d">
    <missing>d</missing>
  </test>
</missing>

请注意,这不会报告第二个列表中任何缺少第一个列表的项目。

使用量化表达式

[编辑 | 编辑源代码]

这可以使用 XQuery 量化表达式重写。有两个原因。首先,XQuery 优化器通常可以比量化表达式更快地运行,而且有些人觉得它们更容易阅读。有关更多详细信息,请参阅XQuery/量化表达式

在这个第二个示例中,列表分配是相同的,但我们只显示列表 1 中缺少列表 2 的项目。

<missing>{
 for $item1 in $list1/item
 return
  if (some $item2 in $list2/item satisfies $item2/text() = $item1/text())
    then ()
    else $item1
}</missing>

这将返回

<missing>
  <item>b</item>
  <item>d</item>
</missing>

我们现在准备将此缺失函数模块化,以便我们可以将任何两个列表传递给它以查找缺失元素。

创建缺失的 XQuery 函数

[编辑 | 编辑源代码]

我们的下一步是创建一个 XQuery 函数,它比较任意两个列表,并返回第二个列表中不在第一个列表中的项目。

declare function local:missing($list1 as node()*, $list2 as node()*) as node()* {
 for $item1 in $list1/item
 let $item-text := $item1/text()
 return
  if (some $item2 in $list2/item satisfies $item2/text() = $item1/text())
    then ()
    else $item1
};

我们可以重写输出函数来使用此函数

<results>
  <missing-from-2>{local:missing($list1, $list2)}</missing-from-2>
  <missing-from-1>{local:missing($list2, $list1)}</missing-from-1>
</results>

请注意,在第二次调用 missing() 函数时,列表的顺序已颠倒。第二次遍历寻找 list2 中不在 list1 中的项目。

运行此查询将生成以下输出

<results>
  <missing-from-2>
    <item>b</item>
    <item>d</item>
  </missing-from-2>
  <missing-from-1>
    <item>e</item>
  </missing-from-1>
</results>

创建 HTML 差异列表

[编辑 | 编辑源代码]

我们可以使用 CSS 来设置这些报告输出的样式。

屏幕图像

[编辑 | 编辑源代码]
使用 CSS 的 HTML 差异报告

示例数据

[编辑 | 编辑源代码]

此示例使用项目的完整单词来显示文本高亮显示

let $list1 :=
<list>
  <item>apples</item>
  <item>bananas</item>
  <item>carrots</item>
  <item>kiwi</item>
</list>


let $list2 :=
<list>
  <item>apples</item>
  <item>carrots</item>
  <item>grapes</item>
</list>

以下函数使用 HTML div 和 span 元素,并为每个缺失的 div 添加 class="missing"。CSS 文件将突出显示此背景。

declare function local:missing($list1 as node()*, $list2 as node()*) as node()* {
 for $item1 in $list1/item
 return
  if (some $item2 in $list2/item satisfies $item2/text() = $item1/text())
    then <div>{$item1/text()}</div>
    else
    <div>
    {attribute {'class'} {'missing'}}
    {$item1/text()}     
    </div>
};

然后我们使用以下 CSS 文件来突出显示差异。每个缺失元素都必须具有 class="missing" 属性,才能在该报告中突出显示缺失元素。

body {font-family: Ariel,Helvetica,sans-serif; font-size: large;}
h2 {padding: 3px; margin: 0px; text-align: center; font-size: large; background-color: silver;}
.left, .right {border: solid black 1px; padding: 5px;}
.missing {background-color: pink;}
.left {float: left; width: 190px}
.right {margin-left: 210px; width: 190px}
<body>
   <h1>Missing Items Report</h1>
   <div class="left">
     <h2>List 1</h2>
     {for $item in $list1/item return <div>{$item/text()}</div>}
   </div>
   <div class="right">
     <h2>List 2</h2>
     {for $item in $list2/item return <div>{$item/text()}</div>}
   </div>
   <br/>
   <div class="left">
     <h2>List 1 Missing from 2</h2>
     {local:missing($list1, $list2)}
   </div>
   <div class="right">
     <h2>List 2 Missing from 1</h2>
     {local:missing($list2, $list1)}
   </div>
  </body>

如果列表是按排序顺序排列的,或者可以排序成顺序排列的,则另一种方法是递归地对两个列表进行排序。核心算法如下所示

declare function local:merge($a, $b as item()* ) as item()* {
  if (empty($a) and empty($b))
  then ()
  else if (empty ($b) or $a[1] lt $b[1])
  then ($a[1], local:merge(subsequence($a, 2), $b))
  else if (empty($a) or $a[1] gt $b[1])
  then ($b[1],local:merge($a, subsequence($b,2)))      
  else (: a and b matched :)
     ($a[1], $b[1], local:merge(subsequence($a,2), subsequence($b,2)))
  };

使用上面的示例,我们可以合并两个列表。

 
let $list1 := 
<list>
  <item>apples</item>
  <item>bananas</item>
  <item>carrots</item>
  <item>kiwi</item>
</list>
 
 
let $list2 := 
<list>
  <item>apples</item>
  <item>carrots</item>
  <item>grapes</item>
</list>

return 
<result>
{local:merge($list1/item,$list2/item) }
</result>

执行

合并上的操作将取决于应用程序,并且可以修改算法以仅输出一个或另一个列表中的不匹配项目,并适当地处理匹配项目。例如,要将合并后的列表显示为 HTML,我们可能会修改算法以

declare function local:merge($a, $b as item()* ) as item()* {
  if (empty($a) and empty ($b)) 
  then ()
  else if (empty ($b) or $a[1] lt $b[1])
  then (<div class="left">{$a[1]/text()}</div>, local:merge(subsequence($a, 2), $b))
  else if (empty ($a) or $a[1] gt $b[1])
  then (<div class="right">{$b[1]/text()}</div>,local:merge($a, subsequence($b,2))) 
  else (<div class="match">{$a[1]/text()}</div>, local:merge(subsequence($a,2), subsequence($b,2)))
 };

执行

华夏公益教科书