跳转到内容

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


执行

华夏公益教科书