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)))
};