跳到內容

XQuery/搜尋、分頁和排序

來自華夏公益教科書

這些示例使用一個簡單的 XML 檔案,其中包含有關全球地震的資料。該資料來自Swivel。示例使用“建立自定義檢視”中介紹的通用表格檢視器進行輸出。

此示例在地震位置中搜索字串。

declare option exist:serialize  "method=xhtml media-type=text/html indent=yes";
import module namespace wikiutil = "http://www.cems.uwe.ac.uk/xmlwiki" at  "util.xqm";

let $search := request:get-parameter("search","")
let $matches := //Earthquake[contains(Location,$search)]
return
<html>
    <head>
         <title>Search Earthquakes for {$search}</title>
     </head>
     <body>
     <h1>Search Earthquakes</h1>
     <form>Search for <input type="text" name="search" value="{$search}"/>
     </form>
     {
       wikiutil:sequence-to-table($matches)
     }
     </body>
   </html>

執行

此指令碼實現搜尋結果的分頁。這裡針對每次呼叫重複完整搜尋,互動狀態儲存在隱藏的輸入中。

declare option exist:serialize  "method=xhtml media-type=text/html indent=yes";
import module namespace  wikiutil = "http://www.cems.uwe.ac.uk/xmlwiki" at  "util.xqm";

let $search := request:get-parameter("search","")
let $start:= xs:integer(request:get-parameter("start", "1"))
let $records := xs:integer(request:get-parameter("records", "5"))
let $action := request:get-parameter("action","search")

let $allMatches := //Earthquake[contains(Location,$search)]

(: compute the limits for this page :)
let $max := count($result)
let $start := 
      if ($action = "Previous") 
      then max(($start - $records, 1))
      else if ($action="Next")
      then  if ($max <$start +$records) 
            then $start 
            else $start +$records
      else if ($action="Search")
      then 1
      else $start
let $end :=  min (($start + $records - 1,$max))
 
(: restrict the full set of matches to this subsequence :)
let $matches := subsequence($allMatches,$start,$records)

return
<html>
      <head>
         <title>Search Earthquakes </title>
     </head>
     <body>
        <h1>Search Earthquakes</h1>
        <form >
              Search Location for <input type="text" name="search" value="{$search}"/> 
              <input type="submit" name="action" value="Search"/>
              <br/>
              <input type="hidden" name="start" value="{$start}"/>
              <input type="submit" name="action" value="Previous"/> 
              <input type="submit" name="action" value="Next"/>
              <p>Displaying {$start} to {$end} out of {$max} records found.</p>
              {wikiutil:sequence-to-table($matches) }
              <p>Records per Page <input type="text" name="records" value="{$records}"/></p>
        </form>
     </body>
</html>

執行

為了對列進行排序,我們在每列新增一個提交按鈕。這需要擴充套件通用表格檢視器以按選定列對節點進行排序。

declare function wikiutil:sequence-to-table($seq,$sort) {
  <table border="1">
     <tr>
         {for $node in $seq[1]/*
         return <th><input type="submit" name="Sort" value="{name($node)}"/></th> 
        }   
        </tr>
      {for $row in $seq
        let $sortBy := data($row/*[name(.) = $sort])
        order by $sortBy
        return
         <tr>
            {for $node in $seq[1]/*
             let $data := data($row/*[name(.)=name($node)])
             return <td>{$data}</td> 
        } 
         </tr>
      }
   </table>
 };
declare option exist:serialize  "method=xhtml media-type=text/html indent=yes";
import module namespace  wikiutil = "http://www.cems.uwe.ac.uk/xmlwiki" at  "util.xqm";

let $search := request:get-parameter("search","")
let $sort := request:get-parameter("Sort","Date")
let $matches := //Earthquake[contains(Location,$search)]
return
<html>
    <head>
       <title>Search Earthquakes}</title>
     </head>
     <body>
       <h1>Search Earthquakes</h1>
       <form>Search Location for <input type="text" name="search" value="{$search}"/>
         {wikiutil:sequence-to-table($matches,$sort)}
       </form>
     </body>
</html>

請注意,排序按字串值進行:按震級排序僅偶然成功,而按死亡人數排序則失敗。

執行

改進之處是允許連續點選列標題以反轉排序方向。這需要在互動狀態中新增兩個更多項,即當前排序順序和當前方向,以及對錶格生成器的更改。理想情況下,我們希望能夠說出類似以下的內容

   for $row ..
   let $sortBy := ..
   let $direction := if (..) then "ascending" else "descending"
   order by $sortBy $direction

但這並非有效的 FLWOR 表示式。相反,我們需要兩個 FLWOR 表示式,一個用於每個方向。

 declare function wikiutil:sequence-to-table($seq,$sort,$direction) {
 <table border="1">
     <tr>
         {for $node in $seq[1]/*
         return <th><input type="submit" name="Sort" value="{name($node)}"/></th> 
        }   
      </tr>
      { if ($direction = 1) 
       then 
        for $row in $seq
        let $sortBy := data($row/*[name(.) = $sort])
        order by $sortBy ascending
        return
         <tr>
            {for $node in $seq[1]/*
             let $data := data($row/*[name(.)=name($node)])
             return <td>{$data}</td> 
           } 
         </tr>
      else 
         for $row in $seq
        let $sortBy := data($row/*[name(.) = $sort])
        order by $sortBy descending
        return
         <tr>
            {for $node in $seq[1]/*
             let $data := data($row/*[name(.)=name($node)])
             return <td>{$data}</td> 
           } 
         </tr>    
      }
   </table>
 };

然後指令碼變為

import module namespace  wikiutil = "http://www.cems.uwe.ac.uk/xmlwiki" at  "util.xqm";

declare option exist:serialize  "method=xhtml media-type=text/html indent=yes";

let $search := request:get-parameter("search","")
let $sort := request:get-parameter("Sort","Date")
let $lastSort := request:get-parameter("LastSort","")
let $lastDirection := number(request:get-parameter("LastDirection","1"))
let $direction := if ($lastSort = $sort) then - $lastDirection else 1
let $matches := //Earthquake[contains(Location,$search)]
return
<html>
    <head>
         <title>Search Earthquakes</title>
     </head>
     <body>
     <h1>Search Earthquakes</h1>
     <form>Search Location for <input type="text" name="search" value="{$search}"/>
     <input type="hidden" name="LastSort" value="{$sort}"/>
     <input type="hidden" name="LastDirection" value="{$direction}"/>
     {  wikiutil:sequence-to-table($matches,$sort, $direction)   }
      </form>
     </body>
</html>

執行

使用表格模式

[編輯 | 編輯原始碼]

透過提供表格模式,可以獲得對輸出的更大控制。此模式可以指定列的順序和列標題,以及潛在的轉換指令。

我們可以將表格模式提供為一系列 Column 定義

  <Schema>
      <Column name="Location" heading="Earthquake location"/>
      <Column name="Magnitude" heading="Magnitude (Richter Scale)"/>
     <Column name="Date" />
   </Schema>

基於模式的函式如下所示

declare function wikiutil:sequence-to-table-with-schema($seq,$schema) {
  <table border="1">
     <tr>
        {for $column in $schema/Column
         return <th>{string( ($column/@heading,$column/@name)[1])}</th> 
        }
     </tr>
      {for $row in $seq
       return
         <tr>
            {for $column in $schema/Column
             let $data := data($row/*[name(.)=$column/@name])
             return <td>{$data}</td> 
           } 
         </tr>
      }
   </table>
 };


請注意,使用了 XQuery 習慣用法來計算列標題,如果提供了標題,則使用該標題,否則使用節點名稱

  ($column/@heading,$column/@name)[1]

這會計算序列中的第一個非空項,這是一種更簡潔且更通用的替代方法,而不是

  if (exists($column/@heading))
  then $column/@heading
  else $column/@name


執行

華夏公益教科書