【问题标题】:Why does this XQuery filter return unwanted elements?为什么这个 XQuery 过滤器会返回不需要的元素?
【发布时间】:2016-03-14 17:23:50
【问题描述】:

我正在使用实现 XQuery 3.1 的 BaseX 8.4.1

我正在尝试在 XQuery 中做一件非常基本的事情,但我似乎无法弄清楚。

我创建了一些示例数据来说明我正在尝试做的事情。我的数据集看起来像这样。它是一个简单的嵌入式结构。有几天,每天都有一些活动,活动有成员。

数据

<root>
    <day>
        <name>1</name>
        <event>
            <name>1</name>
            <member>A</member>
            <member>B</member>
        </event>
        <event>
            <name>2</name>
            <member>C</member>
        </event>
    </day>
    <day>
        <name>2</name>
        <event>
            <name>3</name>
            <member>A</member>
            <member>B</member>
        </event>
        <event>
            <name>4</name>
            <member>C</member>
        </event>
    </day>
    <day>
        <name>3</name>
        <event>
            <name>5</name>
            <member>C</member>
        </event>
    </day>
</root>

我想要做的是获取成员列表,并为每个成员获取他们举办活动的日期以及他们举办的活动的列表。所以结果应该是这样的:

期望的结果

<member>
  <name>A</name>
  <day>
    <name>1</name>
    <event>
      <name>1</name>
      <member>A</member>
      <member>B</member>
    </event>
  </day>
  <day>
    <name>2</name>
    <event>
      <name>3</name>
      <member>A</member>
      <member>B</member>
    </event>
  </day>
</member>
<member>
  <name>B</name>
  <day>
    <name>1</name>
    <event>
      <name>1</name>
      <member>A</member>
      <member>B</member>
    </event>
  </day>
  <day>
    <name>2</name>
    <event>
      <name>3</name>
      <member>A</member>
      <member>B</member>
    </event>
  </day>
</member>
<member>
  <name>C</name>
  <day>
    <name>1</name>
    <event>
      <name>2</name>
      <member>C</member>
    </event>
  </day>
  <day>
    <name>2</name>
    <event>
      <name>4</name>
      <member>C</member>
    </event>
  </day>
  <day>
    <name>3</name>
    <event>
      <name>5</name>
      <member>C</member>
    </event>
  </day>
</member>

为此,我尝试了以下 XQuery:

我尝试了什么

for $member in distinct-values(//member)
return
<member>
  <name>{$member}</name>
    {for $day in //day where $day/event/member = $member
      let $event := $day/event where $event/member = $member
    return 
      <day>
        {$day/name}
        {$event}
    </day>}
</member>

但是,这不会应用过滤。所以我为一个成员保留了他们不是成员的所有事件:

我得到了什么

<member>
  <name>A</name>
  <day>
    <name>1</name>
    <event>
      <name>1</name>
      <member>A</member>
      <member>B</member>
    </event>
    <event>
      <name>2</name>
      <member>C</member>
    </event>
  </day>
  <day>
    <name>2</name>
    <event>
      <name>3</name>
      <member>A</member>
      <member>B</member>
    </event>
    <event>
      <name>4</name>
      <member>C</member>
    </event>
  </day>
</member>
<member>
  <name>B</name>
  <day>
    <name>1</name>
    <event>
      <name>1</name>
      <member>A</member>
      <member>B</member>
    </event>
    <event>
      <name>2</name>
      <member>C</member>
    </event>
  </day>
  <day>
    <name>2</name>
    <event>
      <name>3</name>
      <member>A</member>
      <member>B</member>
    </event>
    <event>
      <name>4</name>
      <member>C</member>
    </event>
  </day>
</member>
<member>
  <name>C</name>
  <day>
    <name>1</name>
    <event>
      <name>1</name>
      <member>A</member>
      <member>B</member>
    </event>
    <event>
      <name>2</name>
      <member>C</member>
    </event>
  </day>
  <day>
    <name>2</name>
    <event>
      <name>3</name>
      <member>A</member>
      <member>B</member>
    </event>
    <event>
      <name>4</name>
      <member>C</member>
    </event>
  </day>
  <day>
    <name>3</name>
    <event>
      <name>5</name>
      <member>C</member>
    </event>
  </day>
</member>

当然,这应该很容易,不是吗?

【问题讨论】:

  • 我将xbase 更改为basex -- 我猜,这就是您的意图。 xbase 与 XML 无关。
  • @JensErat Thx,这就是我的意思。

标签: xquery basex


【解决方案1】:

这是一个非常小的问题。您想过滤事件,并将$events 定义为$day 的所有事件的序列。然后,您使用 =-operator 进行过滤,该运算符已设置语义 - 如果左侧的任何项目(当天的所有事件的成员)等于右侧的任何项目(当前的$member),where 子句的计算结果为true

改为循环遍历事件。

for $member in distinct-values(//member)
return
<member>
  <name>{$member}</name>
    {for $day in //day where $day/event/member = $member
      (: for instead of let :)
      for $event in $day/event where $event/member = $member
    return 
      <day>
        {$day/name}
        {$event}
    </day>}
</member>

使用谓词而不是where 子句通常会导致代码更易于阅读且嵌套显式循环更少。这是一个清理后的示例:

for $member in distinct-values(//member)
return
  <member>{
    <name>{ $member }</name>,
    for $day in //day[event/member = $member]
    return 
      <day>{
        $day/name,
        $day/event[member = $member]
      }</day>
  }</member>

【讨论】:

  • 太好了,谢谢。我终于开始了解 xquery 的工作原理了。
【解决方案2】:

这里是使用group by 两次的替代解决方案,它只扫描一次源数据:

for $member in //member
group by $name := $member/text()
order by $name
return <member>{
    <name>{$name}</name>,
    for $event in $member/..
    group by $day := $event/../name
    order by $day
    return <day>{
        <name>{$day}</name>,
        $event
    }</day>
}</member>

【讨论】:

  • 那么,你认为这会表现得更好吗?
  • 这完全取决于您使用的查询处理器、输入数据的特性和可用的索引等。
  • 可以说 group-by 更容易优化,因为你更明确地表达了你的意图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-03
  • 1970-01-01
  • 2013-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-23
相关资源
最近更新 更多