XQuery / BaseX-限制结果深度

问题描述

使用XPath或XQuery时,是否有办法限制结果的深度?

我正在使用支持XQuery 3.1和XSLT 2.0的BaseX。

例如,给定此输入文档:

<country name="United States">
  <state name="California">
    <county name="Alameda" >
      <city name="Alameda" />
      <city name="Oakland" />
      <city name="Piedmont" />
    </county>
    <county name="Los Angeles">
      <city name="Los Angeles" />
      <city name="Malibu" />
      <city name="Burbank" />
    </county>
    <county name="Marin">
      <city name="Fairfax" />
      <city name="Larkspur" />
      <city name="Ross" />
    </county>
    <county name="Sacramento">
      <city name="Folsom" />
      <city name="Elk grove" />
      <city name="Sacramento" />
    </county>
  </state>
</country>

如果执行以下查询/country/state,则会得到以下结果:

<state name="California">
  <county name="Alameda">
    <city name="Alameda"/>
    <city name="Oakland"/>
    <city name="Piedmont"/>
  </county>
  <county name="Los Angeles">
    <city name="Los Angeles"/>
    <city name="Malibu"/>
    <city name="Burbank"/>
  </county>
  <county name="Marin">
    <city name="Fairfax"/>
    <city name="Larkspur"/>
    <city name="Ross"/>
  </county>
  <county name="Sacramento">
    <city name="Folsom"/>
    <city name="Elk grove"/>
    <city name="Sacramento"/>
  </county>
</state>

我想限制结果的深度。理想情况下,我有一种方法可以指定深度,而不是对XPath查询进行硬编码。

作为示例,我想将结果限制为结果节点及其子节点,但不包括孙子节点,因此结果为:

<state name="California">
  <county name="Alameda" />
  <county name="Los Angeles" />
  <county name="Marin" />
  <county name="Sacramento" />
</state>

解决方法

一种简单明了的方法是将XSLT-2.0与空模板一起使用,以取消<county>的所有子级。 <xsl:strip-space>删除了孩子将要使用的空间。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:strip-space elements="*" />
 
  <!-- Identity template -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="/">
      <xsl:apply-templates select="/country/state" />
  </xsl:template>
  
  <xsl:template match="county/*" />
  
</xsl:stylesheet>

输出为:

<?xml version="1.0" encoding="UTF-8"?>
<state name="California">
    <county name="Alameda"/>
    <county name="Los Angeles"/>
    <county name="Marin"/>
    <county name="Sacramento"/>
</state>

使用XQuery,解决方案可能如下所示:

for $st in doc("b.xml")/country/state return
  element { node-name($st) } { $st/@*,for $ct in $st/county return 
    element { node-name($ct) } { $ct/@* }
  }

输出是相同的。

,

实际上,您的查询结果是单个节点,即源文档中的state节点。然后,某些软件以某种特定格式显示查询的结果(即state节点),但是原则上可以在不更改查询的情况下以不同的方式显示结果。例如,我知道可以将查询结果显示为

的软件
/country[1]/state[1]

因此,您需要分开两个问题:查询返回哪些节点,以及它们如何显示?在某些情况下,创建处理管道可能是有意义的,其中第一步选择感兴趣的节点,第二步控制结果的表示。

就我个人而言,我总是会在XSLT中执行第二步,但是有些人更喜欢XQuery。随便挑。

,

@ zx845的帖子使我步入正轨。我的最终目标是限制结果的深度,以获取“摘要”和必要时需要获得更深入结果的元数据。

BaseX has a function "db:node-id",它将返回任何给定节点的内部节点ID。 another function,"db:open-id"会返回具有给定ID的节点。

假设此给定输入:

<country name="United States">
  <state name="California">
    <county name="Alameda">
      <city name="Alameda"/>
      <city name="Oakland"/>
      <city name="Piedmont"/>
    </county>
    <county name="Los Angeles">
      <city name="Los Angeles"/>
      <city name="Malibu"/>
      <city name="Burbank"/>
    </county>
    <county name="Marin">
      <city name="Fairfax"/>
      <city name="Larkspur"/>
      <city name="Ross"/>
    </county>
    <county name="Sacramento">
      <city name="Folsom"/>
      <city name="Elk Grove"/>
      <city name="Sacramento"/>
    </county>
  </state>
  <state name="New York">
    <county name="Albany">
      <city name="Albany"/>
      <city name="Cohoes"/>
      <city name="Watervliet"/>
    </county>
    <county name="Erie">
      <city name="Buffalo"/>
      <city name="Lackawanna"/>
      <city name="Tonawanda"/>
    </county>
  </state>
</country>

我定义了此函数,它使我可以控制深度,并返回每个节点的节点ID。

declare function local:abbreviated($input,$depth as xs:integer)
{
  if($depth = 0) then
    element node {
      db:node-id($input)
    }
  else
    element { node-name($input) } { 
      attribute node-id {
        db:node-id($input)
      },$input/@*,$input/text(),for $child in $input/*
        return local:abbreviated($child,$depth - 1)
    }
};

如果我执行以下操作:

declare variable $input := /country/state;
for $result in $input
  return local:abbreviated($result,1)

然后我得到这个结果:

<state node-id="3" name="California">
  <node>5</node>
  <node>13</node>
  <node>21</node>
  <node>29</node>
</state>
<state node-id="37" name="New York">
  <node>39</node>
  <node>47</node>
</state>

现在,当我处理结果时,如果用户想要有关state元素的更多详细信息,我可以处理每个'node'元素并执行此查询以获得该节点的实际内容

local:abbreviated(db:open-id('states',5),2)

结果:

<county node-id="5" name="Alameda">
  <city node-id="7" name="Alameda"/>
  <city node-id="9" name="Oakland"/>
  <city node-id="11" name="Piedmont"/>
</county>