跳轉到內容

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

執行

華夏公益教科書