【问题标题】:XPath: finding nodes duplicated n times with a single path expression queryXPath:使用单个路径表达式查询查找重复 n 次的节点
【发布时间】:2014-03-07 01:34:31
【问题描述】:

我正在练习编写一些 XPath 查询,但遇到了一个问题。以下是我正在使用的示例文档:

<dept-db>
  <dept>
    <name>HR</name>
      <emp>
        <name>John</name>
        <country>USA</country>
      </emp>
      <emp>
        <name>Chris</name>
        <country>USA</country>
      </emp>
  </dept>
  <dept>
    <name>Technology</name>
    <emp>
      <name>Oliver</name>
      <country>UK</country>
    </emp>
    <emp>
      <name>Emily</name>
      <country>USA</country>
    </emp>
  </dept>
</dept-db>

我想要实现的是检索其国家在文档中出现两次以上的所有员工。我从一个更简单的查询开始,即应该找到重复的查询:

<!-- language: lang-xsl -->
doc("emp.xml")//emp[preceding::emp/country=./country or following::emp/country=./country]

虽然它返回所有员工(显然 Oliver 不应该在结果中列出)。

我是 XPath 的新手,不太确定我是否了解点 '.' 的概念。说明符正确。我希望上述查询的行为如下:遍历 emp 节点集,并检查在文档中当前节点上方和下方出现的节点中是否存在具有相同国家/地区的员工。

感谢您的解释(应用点说明符来执行 GROUP BY 类型的查询)并帮助使查询正常工作(除非使用单个路径表达式是不可能的?)。如果重要的话,我会使用 eXide(eXist-db 2.1 的一部分)和 XQuery 3.0 来执行查询。

【问题讨论】:

    标签: xpath xquery exist-db


    【解决方案1】:

    由于您可以使用 XQuery 3.0 的 group by 子句,我会这样做。此查询按国家/地区对员工进行分组,并仅返回出现次数超过两次的国家/地区的员工:

    for $employee in //emp
    let $country := $employee/country
    group by $country
    where count($employee) > 2
    return $employee
    

    关于你的方法:

    • 我无法重现您的查询的任何问题。使用 eXist DB 的在线演示,我没有在结果中得到任何“Oliver”。使用 BaseX 和 Zorba 也能正常工作。您确定您的文件中没有第二位英国员工吗?
    • 您写了“其国家出现两次以上”:这是我在上面实现的。查看您的查询,您可能想要“至少两次”?如果是这样,请更改 where 子句以满足您的要求。如果不是,则查询中的问题是您可能希望使用 and 而不是 or,但这将省略该国家/地区的第一位和最后一位员工。

    【讨论】:

    • 感谢您提到 BaseX,我在那里运行了我的查询,它确实有效。以前我使用了本地版本的 eXist-db,显然它在这种情况下会产生不同的结果(以及我检查过的一些其他查询),不知道为什么。至于您提出的查询, group by .. where .. 是正确的表达方式吗? BaseX XQuery 处理器返回 [XPST0003] Expecting valid expression after 'group by'。
    • 这段代码绝对是正确的表达方式。实际上,我在 BaseX 中编写了该代码。您在哪个版本中有问题,您的确切查询是什么?您是否已将文件作为数据库创建并打开,或者您如何访问它?
    • 我已经尝试过 Live demo 和 BaseX 7.0.2(aptitude 存储库中的当前版本)。我创建了一个包含 .xml 文件的数据库并通过 GUI 打开它。
    • 刚刚意识到 XQuery 表达式也使用扩展的 FLWOR 表达式。 BaseX 7.0.2 从 2011 年开始就已经很老了,如果你想使用 BaseX,可以考虑从 basex.org> 手动加载 jar 文件,并有很多改进。
    【解决方案2】:

    在 XPath 2.0 中,您可以这样做

    //emp[count(index-of(//country/text(), country/text())) > 2]
    

    index-of 将指示整个文档中country/text() 出现的索引,然后我们需要做的就是计算它们并检查它们是否超过2。

    【讨论】:

    • 显然问题不在于我的查询本身,因为我在另一个环境中重新运行它并且它有效(请参阅我对 Jens Erat 回答的评论)。谢谢你让我知道这个替代解决方案:)
    【解决方案3】:

    如果您坚持使用 XQuery 1.0,您可以在单个表达式中完成,但您需要将源文档绑定到一个变量。我用过$src。这是因为您有效地访问了源文档两次并加入了谓词:

    $src//emp[let $emp-country := country return count($src//data(country)[. = $emp-country]) > 2]
    

    你也可以重写这个,让它更清楚一点:

    let $all-countries := $src//data(country)
    return
        $src//emp[let $emp-country := country return count($all-countries[. = $emp-country]) > 2]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-27
      • 1970-01-01
      • 1970-01-01
      • 2020-10-12
      • 1970-01-01
      • 2013-06-19
      • 1970-01-01
      相关资源
      最近更新 更多