如何使用翻滚 window 按内容对 XML 元素进行分组?

How to use tumbling window to group XML elements by content?

如何根据 [0-9]tumbling window 的数字是否匹配进行分组?

期望的输出:

...
<record>
    <name>joe</name>
    <data>phone1</data>
    <data>phone2</data>
</record>
...

当前输出,未分组:

<xml>
  <record>
    <person key="$s" data="name">phone1</person>
  </record>
  <record>
    <person key="$s" data="name">phone2</person>
  </record>
  <record>
    <person key="$s" data="name">phone3sue</person>
  </record>
  <record>
    <person key="$s" data="name">cell4</person>
  </record>
  <record>
    <person key="$s" data="name">home5alice</person>
  </record>
  <record>
    <person key="$s" data="name">atrib6</person>
  </record>
  <record>
    <person key="$s" data="name">x7</person>
  </record>
  <record>
    <person key="$s" data="name">y9</person>
  </record>
  <record>
    <person key="$s" data="name">z10</person>
  </record>
</xml>

输入:

<text>
  <line>people</line>
  <line>joe</line>
  <line>phone1</line>
  <line>phone2</line>
  <line>phone3</line>
  <line>sue</line>
  <line>cell4</line>
  <line>home5</line>
  <line>alice</line>
  <line>atrib6</line>
  <line>x7</line>
  <line>y9</line>
  <line>z10</line>
</text>

概念是每个 "person" 将有一个名称(无数字)和可能的附加数据。因此,希望阅读每一行,然后根据找到名称的位置进行分组。

代码:

xquery version "3.0";

<xml>
{
for tumbling window $line in db:open("foo.txt")//text()
start $s when matches($s, '[0-9]')
return   
<record>

       <person key='$s' data="name">{$line}</person>

 </record>
}
 </xml>

查看输出,"phone3sue" 显然在做一些匹配和分组,虽然不完全符合预期,因为 "phone3" 应该在它自己的元素中,嵌套在 "joe" 中而不是"sue"。但是,仍然有一些匹配发生。


来自撒克逊邮件列表:

On Wed, Feb 19, 2020 at 10:31:37AM -0800, thufir scripsit:

I'll re-read the section on windowing; my impression was that it was more for display or report purposes.

开窗是从数据流中提取块的方法。

您得到的实际上是一系列线条元素;你可以 识别 "name" 行,但您现在不知道它们有多远 are/how 任何特定的一对名称之间有很多数据。

Windows 让你说“我想要这个流的块以 名称行并继续(但不包括)下一个名称 行”。

Would you elaborate on what you mean by two steps, a bit more concretely?

您正在尝试获取一些输入 XML 并将其转换为不同的输出 XML.

如果这是纯粹的转换 -- 更改所有名为 FOO 的元素 到名为 BAZ 的元素——XQuery 不是最好的工具选择。在以下情况下使用 XSLT 你可以。它们在计算上是相同的,但语言有 不同的偏见和 XSLT 确实更自然地转换。

如果输出 XML 是输入的抽象表示 -- 道德上的某种报告 -- 抽象然后呈现它有很大帮助。

所以在你的情况下,你拥有的是一个包含隐式 名称和数据之间的关联。 (这是一连串的线条;唯一的 您知道这些数据线与该名称线对应的方式是位置。所以 隐式。)如果你把它变成名称之间的显式映射 和数据——例如通过创建一个映射变量,其中键是 名称行的内容(以某种方式处理了空格)和 每个键的条目是与该名称关联的数据行—— 你已经完成了抽象部分。

然后您可以获取该地图并生成您想要的 XML 输出 它,这比尝试组合 "create new XML" 简单得多 和 "do the abstraction steps"。我发布的最后一件事有一个 将地图转换为元素的示例,但作为模式它只是

地图:钥匙($地图)! {.}{$map(.)}

(如果你有节点或序列,它会变得更复杂 条目,但仅此而已。)

这让事情更接近意义?

-- 格雷登

_______________________________________________ saxon-help 邮件列表存档于 http://saxon.markmail.org/ 撒克逊帮助@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/saxon-help

如果您使用的是 3.0 或 3.1,则使用 FLWOR 表达式的 "window" 子句。像

for tumbling window $w in line
    start $s when matches($s, '[a-z]')
    return <group key="$s">{$w}</group>

未经测试,可能需要更正或改编。

probably 非常接近:

thufir@dur:~/flwor/foo.txt.database$ 
thufir@dur:~/flwor/foo.txt.database$ basex dennis.xq 
<person name="joe">
  <person id="3" x="0" numerical="true">phone1</person>
  <person id="4" x="0" numerical="true">phone2</person>
  <person id="5" x="0" numerical="true">phone3</person>
</person>
<person name="sue">
  <person id="7" x="0" numerical="true">cell4</person>
  <person id="8" x="0" numerical="true">home5</person>
</person>
<person name="alice">
  <person id="10" x="0" numerical="true">atrib6</person>
  <person id="11" x="0" numerical="true">x7</person>
  <person id="12" x="0" numerical="true">y9</person>
  <person id="13" x="0" numerical="true">z10</person>
</person>thufir@dur:~/flwor/foo.txt.database$ 

代码:

xquery version "3.0";

declare variable $XML := <xml>
 <person id="1" x="0" numerical="false">people</person>
 <person id="2" x="0" numerical="false">joe</person>
 <person id="3" x="0" numerical="true">phone1</person>
 <person id="4" x="0" numerical="true">phone2</person>
 <person id="5" x="0" numerical="true">phone3</person>
 <person id="6" x="0" numerical="false">sue</person>
 <person id="7" x="0" numerical="true">cell4</person>
 <person id="8" x="0" numerical="true">home5</person>
 <person id="9" x="0" numerical="false">alice</person>
 <person id="10" x="0" numerical="true">atrib6</person>
 <person id="11" x="0" numerical="true">x7</person>
 <person id="12" x="0" numerical="true">y9</person>
 <person id="13" x="0" numerical="true">z10</person>
</xml> ;

for $P in $XML/person
where $P[@numerical="true"]
let $PREV := $P/preceding-sibling::person[@numerical="false"][1]
group by $PREV
return <person name="{$PREV}" >  { $P } </person>

以下尝试使用 tumbling window,它以不包含任何 ASCII 数字(personname)的任何 line 开头,后跟任何包含至少一个 ASCII 数字(即 data 行):

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";

declare option output:method 'xml';
declare option output:indent 'yes';

<xml>
{
    for tumbling window $person in text/line
    start $name next $data when matches($name, '^[^0-9]+$') and matches($data, '[0-9]')
    return
        <person>
        {
            <name>{ data($name) }</name>,
            tail($person) ! <data>{data()}</data>

        }
        </person>
}    
</xml>

https://xqueryfiddle.liberty-development.net/gWmuPs1

输出有

<?xml version="1.0" encoding="UTF-8"?>
<xml>
   <person>
      <name>joe</name>
      <data>phone1</data>
      <data>phone2</data>
      <data>phone3</data>
   </person>
   <person>
      <name>sue</name>
      <data>cell4</data>
      <data>home5</data>
   </person>
   <person>
      <name>alice</name>
      <data>atrib6</data>
      <data>x7</data>
      <data>y9</data>
      <data>z10</data>
   </person>
</xml>