使用 XQuery/XPath 获取祖先节点的倒序
Get the reverse order of ancestor nodes with XQuery/XPath
我正在寻找一个好的解决方案(简短的表达式,最佳性能)以在 BaseX 上使用 XQuery 3.1 获取所有祖先节点的反向顺序。
现在我正在使用此代码,为给定的 XML 示例获取 dirA/dirA3/dirA31:
xquery version "3.1" encoding "utf-8";
declare variable $files :=
<dir name="dirA">
<file name="fileA_F1"/>
<file name="fileA_F2"/>
<dir name="dirA1">
<file name="fileA1_F1"/>
<file name="fileA1_F2"/>
</dir>
<dir name="dirA2">
<file name="fileA2_F1"/>
<file name="fileA2_F2"/>
</dir>
<dir name="dirA3">
<file name="fileA3_F1"/>
<file name="fileA3_F2"/>
<file name="fileA3_F3"/>
<dir name="dirA31">
<file name="fileA31_F1"/>
<file name="fileA31_F2"/>
<file name="fileA31_F3"/>
</dir>
</dir>
</dir>;
let $path := trace(string-join($files//file[@name='fileA31_F2']/ancestor::dir/@name,'/'))
return()
和此代码得到相反的顺序 dirA31/dirA3/dirA :
let $reversepath := trace(string-join(reverse(tokenize(string-join($files//file[@name='fileA31_F2']/ancestor::dir/@name,'/'),'/')),'/'))
我的意思是,还有其他以相反顺序遍历祖先的 XPath 或 XQuery 表达式吗?
注解:文件节点的name属性值是唯一的
使用 XPath 3.1 和 =>
运算符的新语法 (https://www.w3.org/TR/xpath-31/#id-arrow-operator)
let $file := $files//file[@name='fileA31_F2']
return
$file/ancestor::dir/@name => reverse() => string-join('/')
应该更紧凑和可读,但需要一段时间才能习惯。
另一个建议:
declare function f:reverse-path($n as node()) {
if (exists($n/../@name))
then $n || '/' || f:reverse-path($n/..)
else @name
};
f:reverse-path($files//file[@name='fileA31_F2'])
或者更一般地说,您要求一个以相反顺序遍历祖先的函数(从最内层的祖先开始):
declare function f:reverse-ancestors($n as node()) {
$n ! (., ..!f:reverse-ancestors(.))
};
为了整体或部分总结前面的答案。 (非常感谢作者!):
xquery version "3.1" encoding "utf-8";
declare namespace f="myfunc";
declare default element namespace "f";
declare variable $files :=
<dir name="dirA">
<file name="fileA_F1"/>
<file name="fileA_F2"/>
<dir name="dirA1">
<file name="fileA1_F1"/>
<file name="fileA1_F2"/>
</dir>
<dir name="dirA2">
<file name="fileA2_F1"/>
<file name="fileA2_F2"/>
</dir>
<dir name="dirA3">
<file name="fileA3_F1"/>
<file name="fileA3_F2"/>
<file name="fileA3_F3"/>
<dir name="dirA31">
<file name="fileA31_F1"/>
<file name="fileA31_F2"/>
<dir name="dirA311"/>
<dir name="dirA312">
<file name="fileA312_F1"/>
<file name="fileA312_F2"/>
<file name="fileA312_F3"/>
</dir>
</dir>
</dir>
</dir>;
declare function f:traverse-ancestors($n as node()) {
(: do it from the farthest to the nearest ancestor node :)
f:doItF2N($n),
(: Using 'Simply map operator' (XQuery 3.0) :)
$n ! (., ..!f:traverse-ancestors(.)),
(: do it from the nearest to the farthest ancestor node :)
f:doItN2F($n)
};
declare function f:doItF2N($n as node()) {
trace($n!@name)
};
declare function f:doItN2F($n as node()) {
trace($n!@name)
};
(: Examples :)
let $childNode := $files//dir[file!@name='fileA312_F3']
(: Ex.1 :)
(: traverse ancestors 'normally' or in reverse order :)
let $x := f:traverse-ancestors($childNode)
(: Ex.2 - Concatenation of the ancestors name attribute in reverse order, using '/' as separator :)
(: Solution with "=>" - "Arrow operator" (XPath 3.1) :)
let $x := trace($childNode/ancestor::dir/@name => reverse() => string-join('/'))
(: Solution with nested function calls (XPath 3.0) :)
let $x := trace(string-join(reverse($childNode/ancestor::dir/@name),'/'))
let $res := ()
return($res)
在 BaseX v9.2.4 中评估为:
name="dirA312"
name="dirA31"
name="dirA3"
name="dirA"
name="dirA"
name="dirA3"
name="dirA31"
name="dirA312"
"dirA31/dirA3/dirA"
"dirA31/dirA3/dirA"
我正在寻找一个好的解决方案(简短的表达式,最佳性能)以在 BaseX 上使用 XQuery 3.1 获取所有祖先节点的反向顺序。
现在我正在使用此代码,为给定的 XML 示例获取 dirA/dirA3/dirA31:
xquery version "3.1" encoding "utf-8";
declare variable $files :=
<dir name="dirA">
<file name="fileA_F1"/>
<file name="fileA_F2"/>
<dir name="dirA1">
<file name="fileA1_F1"/>
<file name="fileA1_F2"/>
</dir>
<dir name="dirA2">
<file name="fileA2_F1"/>
<file name="fileA2_F2"/>
</dir>
<dir name="dirA3">
<file name="fileA3_F1"/>
<file name="fileA3_F2"/>
<file name="fileA3_F3"/>
<dir name="dirA31">
<file name="fileA31_F1"/>
<file name="fileA31_F2"/>
<file name="fileA31_F3"/>
</dir>
</dir>
</dir>;
let $path := trace(string-join($files//file[@name='fileA31_F2']/ancestor::dir/@name,'/'))
return()
和此代码得到相反的顺序 dirA31/dirA3/dirA :
let $reversepath := trace(string-join(reverse(tokenize(string-join($files//file[@name='fileA31_F2']/ancestor::dir/@name,'/'),'/')),'/'))
我的意思是,还有其他以相反顺序遍历祖先的 XPath 或 XQuery 表达式吗?
注解:文件节点的name属性值是唯一的
使用 XPath 3.1 和 =>
运算符的新语法 (https://www.w3.org/TR/xpath-31/#id-arrow-operator)
let $file := $files//file[@name='fileA31_F2']
return
$file/ancestor::dir/@name => reverse() => string-join('/')
应该更紧凑和可读,但需要一段时间才能习惯。
另一个建议:
declare function f:reverse-path($n as node()) {
if (exists($n/../@name))
then $n || '/' || f:reverse-path($n/..)
else @name
};
f:reverse-path($files//file[@name='fileA31_F2'])
或者更一般地说,您要求一个以相反顺序遍历祖先的函数(从最内层的祖先开始):
declare function f:reverse-ancestors($n as node()) {
$n ! (., ..!f:reverse-ancestors(.))
};
为了整体或部分总结前面的答案。 (非常感谢作者!):
xquery version "3.1" encoding "utf-8";
declare namespace f="myfunc";
declare default element namespace "f";
declare variable $files :=
<dir name="dirA">
<file name="fileA_F1"/>
<file name="fileA_F2"/>
<dir name="dirA1">
<file name="fileA1_F1"/>
<file name="fileA1_F2"/>
</dir>
<dir name="dirA2">
<file name="fileA2_F1"/>
<file name="fileA2_F2"/>
</dir>
<dir name="dirA3">
<file name="fileA3_F1"/>
<file name="fileA3_F2"/>
<file name="fileA3_F3"/>
<dir name="dirA31">
<file name="fileA31_F1"/>
<file name="fileA31_F2"/>
<dir name="dirA311"/>
<dir name="dirA312">
<file name="fileA312_F1"/>
<file name="fileA312_F2"/>
<file name="fileA312_F3"/>
</dir>
</dir>
</dir>
</dir>;
declare function f:traverse-ancestors($n as node()) {
(: do it from the farthest to the nearest ancestor node :)
f:doItF2N($n),
(: Using 'Simply map operator' (XQuery 3.0) :)
$n ! (., ..!f:traverse-ancestors(.)),
(: do it from the nearest to the farthest ancestor node :)
f:doItN2F($n)
};
declare function f:doItF2N($n as node()) {
trace($n!@name)
};
declare function f:doItN2F($n as node()) {
trace($n!@name)
};
(: Examples :)
let $childNode := $files//dir[file!@name='fileA312_F3']
(: Ex.1 :)
(: traverse ancestors 'normally' or in reverse order :)
let $x := f:traverse-ancestors($childNode)
(: Ex.2 - Concatenation of the ancestors name attribute in reverse order, using '/' as separator :)
(: Solution with "=>" - "Arrow operator" (XPath 3.1) :)
let $x := trace($childNode/ancestor::dir/@name => reverse() => string-join('/'))
(: Solution with nested function calls (XPath 3.0) :)
let $x := trace(string-join(reverse($childNode/ancestor::dir/@name),'/'))
let $res := ()
return($res)
在 BaseX v9.2.4 中评估为:
name="dirA312"
name="dirA31"
name="dirA3"
name="dirA"
name="dirA"
name="dirA3"
name="dirA31"
name="dirA312"
"dirA31/dirA3/dirA"
"dirA31/dirA3/dirA"