跳至内容

内容管理系统的 XQuery/站点地图

来自维基教科书,开放的书籍,开放的世界

您想使用 eXist 管理您的网站,并将每个集合用作 Web 内容文件夹。您需要一种方法来自动创建网站地图,以便在 Web 根文件夹中添加新集合时,网站导航菜单会自动更新。

我们将使用 eXist 的 get-child-collections() 函数来获取根集合的所有子集合。我们创建一个递归函数来遍历集合树。

来自 eXist 函数库,以下是 get-child-collections 函数的描述。

xmldb:get-child-collections($a as xs:string) xs:string*

Returns a sequence of strings containing all the child collections of the collection specified in $a.
The collection parameter can either be a simple collection path or an XMLDB URI.

如果我们有一个名为 /db/webroot 的集合,我们可以将此字符串作为参数传递给此函数,所有子集合将作为字符串序列返回。

然后我们可以创建一个递归函数,该函数作用于这些子集合中的每一个。

get-child-collections() 的示例用法

[编辑 | 编辑源代码]

以下是用 get-child-collections() 函数的简单示例。您只需传递一个参数,该参数是集合的路径。它将返回该集合中所有子集合的序列。

xquery version "1.0";

let $children := xmldb:get-child-collections('/db/webroot')

return
<results>
   <children>{$children}</children>
</results>

站点地图函数:版本 1

[编辑 | 编辑源代码]
declare function local:sitemap($collection as xs:string) as node()* {
   if (empty(xmldb:get-child-collections($collection)))
      then ()
      else
         <ol>{
            for $child in xmldb:get-child-collections($collection)
            return
               <li>
                  <a href="{concat('/exist/rest', $collection, '/', $child)}">{$child}</a>
                  {local:sitemap(concat($collection, '/', $child))}
               </li>
        }</ol>
};

这个递归函数接受一个字符串作为输入参数,并返回一个复杂的节点。结果是 HTML 有序列表结构。它首先进行测试以查看集合中是否存在任何子元素。如果不存在,它只返回。如果有新的子元素,则它创建一个新的有序列表,并遍历该集合中的所有子元素,为每个子元素创建一个新的列表项,然后调用自身。请注意,这本可以写成,使条件运算符仅在集合中存在子元素时才调用自身。

这是一个关于 尾递归 的示例。这种模式在 XQuery 函数中经常出现。

示例站点地图程序

[编辑 | 编辑源代码]

我们现在可以在 XHTML 页面模板中调用此程序来创建一个网页

源代码

[编辑 | 编辑源代码]
xquery version "1.0";
declare option exist:serialize "method=xhtml media-type=text/html indent=yes";

declare function local:sitemap($collection as xs:string) as node()* {
   if (empty(xmldb:get-child-collections($collection)))
      then ()
      else
         <ol>{
            for $child in xmldb:get-child-collections($collection)
            return
               <li>
                  <a href="{concat('/exist/rest', $collection, '/', $child)}">{$child}</a>
                  {local:sitemap(concat($collection, '/', $child))}
               </li>
        }</ol>
};

<html>
   <head>
      <title>Sitemap</title>
   </head>
   <body>
      <h1>Sitemap for collection /db/webroot</h1>
      {local:sitemap('/db/webroot')}
   </body>
</html>

添加标题

[编辑 | 编辑源代码]

有时导航栏的标题将不同于集合的名称。按照惯例,集合名称通常只是没有空格或大写字母的简短小写字母。导航栏通常具有包含空格和大写字母的标签。

以下示例使用查找表从 XML 文件中查找标题。

xquery version "1.0";
declare function local:sitemap($collection as xs:string) as node()* {
if (empty(xmldb:get-child-collections($collection)))
  then ()
  else
   <ol>{
      for $child in xmldb:get-child-collections($collection)
      let $db-path := concat($collection, '/', $child)
      let $path := concat('/exist/rest', $collection, '/', $child)
      let $lookup :=
doc('/db/apps/sitemap/06-collection-titles.xml')/code-table/item[$db-path=path]/title/text()
      order by $child
      return
         <li>
            <a href="{if (empty($lookup))
                      then ($path)
                      else (concat($path, "/index.xhtml"))}">
                      {if (empty($lookup)) then ($child) else ($lookup)}
            </a>
           {local:sitemap(concat($collection, '/', $child))}
         </li>
   }</ol>
};

屏幕图像

[编辑 | 编辑源代码]

请注意,子集合都是按字母顺序排序的。在某些情况下,这可能不是您希望显示网站导航菜单的顺序。您可以向显示标题的 XML 文件添加一个 sort-order 参数元素,并使用该字段对子集合进行排序。

集合标题文件

[编辑 | 编辑源代码]

将此文件放在以下位置:/db/apps/sitemap/06-collection-titles.xml

<code-table>
    <item>
        <path>/db/webroot/about</path>
        <title>About</title>
    </item>
    <item>
        <path>/db/webroot/training</path>
        <title>Training</title>
    </item>
    <item>
        <path>/db/webroot/faqs</path>
        <title>Frequently Asked Questions</title>
    </item>
    <item>
        <path>/db/webroot/training/xforms</path>
        <title>XForms</title>
    </item>
    <item>
        <path>/db/webroot/training/rest</path>
        <title>ReST</title>
    </item>
    <item>
        <path>/db/webroot/training/xquery</path>
        <title>XQuery</title>
    </item>
    <item>
        <path>/db/webroot/training/tei</path>
        <title>Text Encoding Initiative</title>
    </item>
    <item>
        <path>/db/webroot/training/exist</path>
        <title>eXist</title>
    </item>
    <item>
        <path>/db/webroot/products</path>
        <title>Products</title>
    </item>
    <item>
        <path>/db/webroot/support</path>
        <title>Support</title>
    </item>
</code-table>

自定义站点地图函数

[编辑 | 编辑源代码]

并非所有集合都应该显示在站点地图中。某些集合可能包含您不想在公共站点地图中显示的私有管理数据。有两种方法可以处理这个问题。您可以将一个通用的“已发布集合”列表保存在单独的 XML 文件中。或者,您可以在每个集合中存储一个小的 XML 文件来显示该集合的属性。默认情况下,此集合可以是公共的或私有的,具体取决于您编写函数的方式。

如果您的同事和您各自都在构建希望共享的 Web 应用程序,则第二种选择更具可移植性。通过在您的 collection-properties.xml 文件上进行标准化,您可以将属性存储在集合中,然后通过交换可以压缩的文件夹来交换它们与其他 eXist 站点。

计算集合和子集合中的文件

[编辑 | 编辑源代码]

以下是伪代码

declare function local:count-files-in-collection($collection as xs:string) as xs:integer {
let $child-collections := xmldb:get-child-collections($collection)
   if (empty($child-collections))
      then
         (: return the count of number of files in this collection :)
      else 
         (: for each subcollection call local:count-files-in-collection($child) :)
};
华夏公益教科书