在 XQuery 中更新计数器
Posted
技术标签:
【中文标题】在 XQuery 中更新计数器【英文标题】:Updating counter in XQuery 【发布时间】:2012-05-04 21:34:21 【问题描述】:我想在 xquery 中创建一个计数器。我最初的尝试如下所示:
let $count := 0
for $prod in $collection
let $count := $count + 1
return
<counter>$count </counter>
预期结果:
<counter>1</counter>
<counter>2</counter>
<counter>3</counter>
实际结果:
<counter>1</counter>
<counter>1</counter>
<counter>1</counter>
$count
变量更新失败或被重置。为什么我不能重新分配现有变量?获得预期结果的更好方法是什么?
【问题讨论】:
【参考方案1】:不可变变量
XQuery 是一个functional programming language,其中涉及不可变变量,因此您无法更改变量的值。另一方面,强大的功能集合供您使用,解决了很多日常编程问题。
let $count := 0
for $prod in $collection]
let $count := $count + 1
return
<counter>$count </counter>
let $count
在第 1 行中在所有范围内定义了这个变量,在这种情况下,这些都是以下行。第 3 行中的let $count
定义了一个新的$count
,即0+1
,在此代码块内的所有后续行中均有效-未定义。所以你确实将$count
加了三倍,但立即丢弃结果。
BaseX'查询信息显示了此查询的优化版本
for $prod in $collection
return element "counter" 1
解决办法
要获取$collection
中的元素总数,可以使用
return count($collection)
有关 XQuery 函数的列表,您可以查看 XQuery part of functx,它既包含 XQuery 函数列表,也包含其他一些可以作为模块包含的有用函数。
【讨论】:
我不确定您的示例是否运行,或者,至少,我无法运行它。你能详细说明吗?【参考方案2】:尝试使用 'at':
for $d at $p in $collection
return
element counter $p
这将为您提供每个“$d”的位置。如果您想将它与order by
子句一起使用,这将不起作用,因为位置基于初始顺序,而不是排序结果。为了克服这个问题,只需将 FLWOR 表达式的排序结果保存在一个变量中,并在第二个 FLWOR 中使用 at
子句,该子句只是迭代第一个排序结果。
let $sortResult := for $item in $collection
order by $item/id
return $item
for $sortItem at $position in $sortResult
return <item position="$position"> ... </item>
【讨论】:
【参考方案3】:正如@Ranon 所说,所有 XQuery 值都是不可变的,因此您无法更新变量。但是如果你真的需要一个可更新的数字(不应该太频繁),你可以使用递归:
declare function local:loop($seq, $count)
if(empty($seq)) then ()
else
let $prod := $seq[1],
$count := $count + 1
return (
<count> $count </count>,
local:loop($seq[position() > 1], $count)
)
;
local:loop($collection, 0)
这完全符合您对示例的预期。
在 XQuery 3.0 中,甚至在标准库中定义了此函数的更通用版本:fn:fold-right($f, $zero, $seq)
也就是说,在您的示例中,您绝对应该使用@tohuwawohu 所示的at $count
。
【讨论】:
【参考方案4】:上述所有解决方案都有效,但我想提一下,您可以使用 XQuery Scripting 扩展来设置变量值:
variable $count := 0;
for $prod in (1 to 10)
return
$count := $count + 1;
<counter>$count</counter>
您可以在http://www.zorba-xquery.com/html/demo#twh+3sJfRpHhZR8pHhOdsmqOTvQ= 现场试用此示例
【讨论】:
XQuery Scripting 是一些 XQuery 实现提供的扩展。如果您想同时进行更新和返回元素(例如,对于 Web 服务),则特别好,但会阻止许多可能的优化。您应该尽可能坚持使用基本的 XQuery。 现场示例已过期(404):( 根据@JensErat 的评论,这至少与BaseX
一起崩溃——但还有什么完成的?【参考方案5】:
特定于 MarkLogic,您也可以使用 xdmp:set
。但这打破了函数式语言的假设,因此请谨慎使用。
http://docs.marklogic.com/5.0doc/docapp.xqy#display.xqy?fname=http://pubs/5.0doc/apidoc/ExsltBuiltins.xml&category=Extension&function=xdmp:set
对于实际代码中的xdmp:set
示例,搜索解析器https://github.com/mblakele/xqysp/blob/master/src/xqysp.xqy 可能会有所帮助。
【讨论】:
【参考方案6】:我认为您正在寻找类似的东西:
XQUERY:
for $x in (1 to 10)
return
<counter>$x</counter>
输出:
<counter>1</counter>
<counter>2</counter>
<counter>3</counter>
<counter>4</counter>
<counter>5</counter>
<counter>6</counter>
<counter>7</counter>
<counter>8</counter>
<counter>9</counter>
<counter>10</counter>
【讨论】:
【参考方案7】:使用xdmp:set
代替下面的查询
let $count := 0
for $prod in (1 to 4)
return ( xdmp:set($count,number($count+1)) ,<counter>$count </counter>
【讨论】:
以上是关于在 XQuery 中更新计数器的主要内容,如果未能解决你的问题,请参考以下文章