You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XQuery tumbling window如何按首组起始项正确分组排除前缀误匹配

问题描述

在BaseX 9.7.3环境中,已通过tumbling window子句生成了排序后的名称列表,原始数据片段如下:

<data>
  <group>
    <key id="0c7b0bca-0349-489c-b45f-2612f3134a76">ovid</key>
    <key id="f77ab9c2-0be3-4348-809d-ab245e630f81">ovid 43 b c-17 or 18 a d</key>
  </group>
  <group>
    <key id="39b9d6c2-85a5-4c72-a83e-2a52e548fc3b">ovid 43 bc</key>
    <key id="acf5b3c0-8fd4-4e0c-950b-a40683bab431">ovid 43 bc-17 ad</key>
    <key id="cc57be53-9ca8-4b5e-97cf-1aeca798cded">ovid 43 bc-17 ad or 18 a</key>
    <key id="8395e750-1e52-4152-9d37-8c8f4e389fd3">ovid 43 bc-17 ad or 18 ad</key>
  </group>
  <group>
    <key id="0be07fc6-d9bf-4d56-8352-1885b4dd6574">ovid 43 bc-17 or 18</key>
    <key id="e3aafc69-56b0-4632-a96c-26ca448c6c2d">ovid 43 bc-17 or 18 ad</key>
  </group>
  <group>
    <key id="f9615365-4a32-442b-9e20-9c5abb0e6fa0">ovide</key>
    <key id="c7b45a8d-79a3-4e79-b32b-8d918f67a7b0">ovide 0043 av j-c-0017</key>
  </group>
</data>

需要对该数据做进一步分组:所有ovid开头的条目归为第一组(从"ovid"开始,到"ovid 43 bc-17 or 18 ad"结束),ovide开头的条目单独分为第二组,期望输出结构如下:

<data>
  <group>
    <key id="0c7b0bca-0349-489c-b45f-2612f3134a76">ovid</key>
    <key id="f77ab9c2-0be3-4348-809d-ab245e630f81">ovid 43 b c-17 or 18 a d</key>  
    <key id="39b9d6c2-85a5-4c72-a83e-2a52e548fc3b">ovid 43 bc</key>
    <key id="acf5b3c0-8fd4-4e0c-950b-a40683bab431">ovid 43 bc-17 ad</key>
    <key id="cc57be53-9ca8-4b5e-97cf-1aeca798cded">ovid 43 bc-17 ad or 18 a</key>
    <key id="8395e750-1e52-4152-9d37-8c8f4e389fd3">ovid 43 bc-17 ad or 18 ad</key>  
    <key id="0be07fc6-d9bf-4d56-8352-1885b4dd6574">ovid 43 bc-17 or 18</key>
    <key id="e3aafc69-56b0-4632-a96c-26ca448c6c2d">ovid 43 bc-17 or 18 ad</key>
  </group>
  <group>
    <key id="f9615365-4a32-442b-9e20-9c5abb0e6fa0">ovide</key>
    <key id="c7b45a8d-79a3-4e79-b32b-8d918f67a7b0">ovide 0043 av j-c-0017</key>
  </group>
</data>

当前编写的查询语句执行后仅复现原输入文档,无法实现预期分组效果,原有错误代码如下:

<data>{
  for tumbling window $entry in /*/group/key  
  start $s at $sp previous $sprev next $snext when starts-with($snext, $s)
  end $e at $ep next $enext when not(starts-with($enext, $e)) 
  return  
    <group>{
      for $k in $entry
      return (
        <key id="{$k/@id}">{data($k)}</key>
      )      
    }</group>         
}</data>

核心诉求:是否可以通过对比窗口起始项的完整词元,匹配所有以该完整词元开头的后续条目完成分组,排除ovide这类字符串前缀部分匹配但不属于同组的条目?

解决方案

原有逻辑存在两个核心问题:

  1. 窗口起止判断逻辑错误,现有写法拿相邻项做前缀匹配,没有锚定每个分组的根词元,窗口边界判断完全失效
  2. 前缀匹配没有做词边界校验,starts-with("ovide", "ovid")会返回true,直接导致不同根词的条目被错误归为同组

可以直接使用以下查询实现需求,核心逻辑是提取每个key的首个完整词元作为分组锚点,再做窗口聚合:

<data>{
  let $all-keys := /*/group/key
  for tumbling window $entry in $all-keys
    start $s when true()
    end $e next $n when 
      not($n)
      or tokenize($n, '\s+')[1] != tokenize($s, '\s+')[1]
  return <group>{
    $entry/<key id="{@id}">{data(.)}</key>
  }</group>
}</data>

逻辑说明:

  • 用tokenize(内容, '\s+')[1]提取每个key的首个完整词元,彻底避免ovid和ovide的误匹配
  • 窗口从第一个条目自动启动,只要下一个条目的首词元和当前组起始项的首词元一致,就持续归入当前组,不一致就结束当前窗口开启新窗口
  • 到序列最后一个条目时自动收尾,不会漏项

如果需要适配更多分隔符场景(比如根词元后接横杠、特殊符号等),可以把结束判断替换为正则匹配规则,兼容性更强:

end $e next $n when
  not($n)
  or not(matches($n, '^' || quote:regex-escape(tokenize($s, '\s+')[1]) || '(\s|$)'))

内容的提问来源于stack exchange,提问作者tat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:57:08