XQuery / BaseX - 限制结果的深度
XQuery / BaseX - Limit depth of result
使用 XPath 或 XQuery 时,有没有办法限制结果的深度?
我正在使用 BaseX,它支持 XQuery 3.1 和 XSLT 2.0。
例如,给定此输入文档:
<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>
的所有 children。 <xsl:strip-space>
删除了 children 所使用的 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]
所以你需要分开两个问题:查询的是什么节点return,它们是如何显示的?在某些情况下,创建一个处理管道可能有意义,其中第一步选择感兴趣的节点,第二步控制结果的呈现。
就我个人而言,我总是会在 XSLT 中执行第二步,但有些人更喜欢 XQuery。任你选。
@zx845 的 post 让我走上了正轨。我的最终目标是限制结果的深度,目的是获得“摘要”和必要时获得更深入结果所需的元数据。
BaseX has a function "db:node-id" which will return the internal node ID of any given node. There's another function, "db:open-id" 其中 return 是具有给定 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>
我定义了这个函数,它让我可以控制深度,return node-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>
使用 XPath 或 XQuery 时,有没有办法限制结果的深度?
我正在使用 BaseX,它支持 XQuery 3.1 和 XSLT 2.0。
例如,给定此输入文档:
<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>
的所有 children。 <xsl:strip-space>
删除了 children 所使用的 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]
所以你需要分开两个问题:查询的是什么节点return,它们是如何显示的?在某些情况下,创建一个处理管道可能有意义,其中第一步选择感兴趣的节点,第二步控制结果的呈现。
就我个人而言,我总是会在 XSLT 中执行第二步,但有些人更喜欢 XQuery。任你选。
@zx845 的 post 让我走上了正轨。我的最终目标是限制结果的深度,目的是获得“摘要”和必要时获得更深入结果所需的元数据。
BaseX has a function "db:node-id" which will return the internal node ID of any given node. There's another function, "db:open-id" 其中 return 是具有给定 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>
我定义了这个函数,它让我可以控制深度,return node-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>