在 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 中更新计数器的主要内容,如果未能解决你的问题,请参考以下文章

xquery 随机选择文件而不重复选择

XQuery:将日期时间与毫秒进行比较

在 Oracle 中使用计数器更新字段

如何在 react-native 中更新 Tab Navigator 中徽章计数器的状态

firebase 统计在线用户并在全球计数器中更新

通过 Hibernate 更新计数器