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 函数,它比较任意两个列表,并返回第二个列表中不在第一个列表中的项目。
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>
我们可以使用 CSS 来设置这些报告输出的样式。
此示例使用项目的完整单词来显示文本高亮显示
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)))
};