XQuery:如何计算一个值按顺序出现的次数

XQuery: How to count how many times a value occurs in sequence

我知道函数 count 可以用来计算给定序列中元素的数量,如下所示:

count(result/actors/actor)

在此XML:

<result>
    <actors>
        <actor id="00000015">Anderson, Jeff</actor>
        <actor id="00000030">Bishop, Kevin</actor>
        <actor id="0000000f">Bonet, Lisa</actor>
        <actor id="916503207">Parillaud, Anne</actor>
        <actor id="916503208">Pitt, Brad</actor>
        <actor id="916503209">Freeman, Morgan</actor>
        <actor id="916503211">Domingo, Placido</actor>
        <actor id="916503210">Sharif, Omar</actor>
        <actor id="1337">Doqumenteriet2011</actor>
    </actors>
</result>

但是,如果我想知道某个值在给定序列中出现了多少次怎么办?

例如,如果我想知道以下每个演员 (actorRef) 出现了多少部电影 XML:

<videos>
    <video id="id1235AA0">
        <title>The Fugitive</title>
        <actorRef>00000003</actorRef>
        <actorRef>00000006</actorRef>
    </video>
    <video id="id1244100">
        <title>Enemy of the State</title>
        <actorRef>00000009</actorRef>
        <actorRef>0000000c</actorRef>
        <actorRef>0000000f</actorRef>
        <actorRef>00000012</actorRef>
    </video>
    <video id="id124E230">
        <title>Clerks</title>
        <actorRef>00000015</actorRef>
        <actorRef>00000018</actorRef>
        <actorRef>0000001b</actorRef>
    </video>

我可以很容易地生成所有出现的演员的列表,甚至可以让他们在我制作的序列中出现的次数与 XML:

result/videos//actorRef

但我无法做任何类似的事情,例如 COUNT() 和 GROUP BY 在 SQL 中一起做的事情,通过计算他们在由XQuery 行上方。

我怎样才能生成这个列表?

PS: 最终目标是找到出现次数最多的演员。

当您只存储视频中的演员列表时,这种问题不适合文档存储。我建议还存储演员所属的视频列表。然后你只需要查询拥有最多视频元素的演员。

综上所述,您可以使用您拥有的数据来完成它,但速度不会那么快。您首先需要获取演员的距离列表。然后查询每个演员过滤具有该演员的视频并进行计数。然后按计数排序。

let $actors := fn:distinct-values($results/videos/video/actorRef)

for $actor in $actors
let $count := fn:count($results/videos/video[actorRef = $actor])
Order by $count
return ($actor, $count)

Tyler 的回答是您最终要实现的目标的最佳解决方案,所以我会接受它,但要回答如何计算一个值在序列中出现的次数的具体问题:您可以在序列上使用谓词来创建一个新序列,该序列仅包含与您关心的值匹配的值,然后计数:

let $actors := result/videos//actorRef
for $actor in distinct-values($actors)
return
  ($actor, count($actors[. = $actor]))

这是一个纯 XPath 2.0 表达式(XPath 2.0 是 XQuery 的一个真子集),它生成 actorRef 值的序列来标识参与最多电影数量的演员

 for $maxMovies in 
       max(for $actorId in distinct-values(/*/*/actorRef) 
            return
               count(index-of(/*/*/actorRef, $actorId))
           )
    return 
      (/*/*/actorRef)[index-of(/*/*/actorRef, .)[$maxMovies]]/string()

在以下源 XML 文档上计算此表达式时:

<videos>
    <video id="id1235AA0">
        <title>The Fugitive</title>
        <actorRef>00000003</actorRef>
        <actorRef>00000009</actorRef>
        <actorRef>0000000x</actorRef>
    </video>
    <video id="id1244100">
        <title>Enemy of the State</title>
        <actorRef>00000009</actorRef>
        <actorRef>0000000c</actorRef>
        <actorRef>0000000f</actorRef>
        <actorRef>00000012</actorRef>
    </video>
    <video id="id124E230">
        <title>Clerks</title>
        <actorRef>00000015</actorRef>
        <actorRef>00000018</actorRef>
        <actorRef>0000001b</actorRef>
    </video>
</videos>

产生了正确的、想要的结果:

00000009

使用 XPath 3.0(XQuery 3.0 的适当子集)甚至可以写得更短:

let $vSeq := /*/*/actorRef/string()
  return
    for $maxMovies in 
       max(for $actorId in distinct-values($vSeq) 
            return
              index-of($vSeq, $actorId) ! last()
           )
      return 
        $vSeq[index-of($vSeq, .)[$maxMovies]]

这可以使用简单的映射运算符 (!) 进一步缩短,以避免任何 for-expression:

let $vSeq := /*/*/actorRef/string(),
    $maxOccurs := 
      max(distinct-values($vSeq) ! count(index-of($vSeq, .)) ) 
  return 
    $vSeq[index-of($vSeq, .)[$maxOccurs]]