【问题标题】:Chained XPath axes with trailing `[1]` or `[last()]` predicates带有尾随 `[1]` 或 `[last()]` 谓词的链式 XPath 轴
【发布时间】:2017-03-09 08:58:00
【问题描述】:

这个问题专门关于在 XSLT 2.0 和 Saxon 中使用 XPath。

以[1] 结尾的XPaths

对于 XPath 之类的

following-sibling::foo[1]
descendant::bar[1]

我想当然地认为,Saxon 不会遍历整个轴,而是在找到第一个匹配节点时停止 - 在以下情况下至关重要:

following-sibling::foo[some:expensivePredicate(.)][1]
 

我认为这样的 XPath 也是如此:

(following-sibling::foo/descendant::bar)[1]

即Saxon 不会在选择集合中的第一个节点之前编译与 following-sibling::foo/descendant::bar 匹配的整个节点集。相反,它会(即使对于链式轴)在第一个匹配节点处停止。

以[last()] 结尾的XPaths

现在变得有趣了。当在树中“倒退”时,我假设 XPaths 像

preceding-sibling::foo[1]

工作效率与 following-sibling 等效。但是当链接轴时会发生什么,例如

(preceding-sibling::foo/descendant::bar)[last()]

因为我们需要在这里使用[last()] 而不是[1],

  • Saxon 是否会编译整个节点集以计算它们以获得last() 的数值?
  • 或者它会很聪明,在找到匹配的后代时停止迭代preceding-sibling 轴?
  • 或者它会更聪明并反向迭代descendant 轴以更有效地找到最后一个后代?

【问题讨论】:

    标签: xpath xslt-2.0 saxon


    【解决方案1】:

    Saxon 有多种评估last() 的策略。当用作谓词时,意思是[position()=last()],它通常被翻译成一个内部函数[isLast()],可以通过单项前瞻来评估。 (因此,在您的(preceding-sibling::foo /descendant::bar)[last()] 示例中,它不会在内存中构建节点集,而是一个接一个地读取节点,当它到达末尾时,返回它找到的最后一个)。

    在其他情况下,特别是在 XSLT 匹配模式中使用时,Saxon 会将 child::x[last()] 转换为 child::x[not(following-sibling::x)]。

    当这些方法都不起作用时,多年来,Saxon 有两种策略来评估 last(),具体取决于它所应用于的表达式:(a) 有时它会评估表达式两次,第一次计算节点并返回它们第二次; (b) 在其他情况下,它将所有节点读入内存。我们最近遇到了策略 (a) 失败的情况:请参阅 https://saxonica.plan.io/issues/3122,因此我们一直在做 (b)。

    last() 表达式可能很昂贵,应尽可能避免使用。例如经典的“在相邻项之间插入分隔符”,经常写成

    xx
    if (position() != last()) sep
    

    最好写成

    if (position() != 1) sep
    xx
    

    即而不是在除最后一项之外的每个项目之后插入分隔符,而是在除第一个之外的每个项目之前插入分隔符。或使用string-join,或xsl:value-of/@separator。

    【讨论】:

    • “last() 表达式可能很昂贵”——这是我希望可以消除的预感。当应用于大型数据集中的每个元素时,似乎使用(preceding-sibling::foo/descendant::bar)[last()] 向后看可能会以 O(n²) 执行。对于我的应用程序,基于具有前瞻性use 属性的键的替代方案可能是可能的,它将以二次性能换取线性内存使用。在大型数据集上仍然不理想,但易于管理。
    • 在循环中应用时,任何使用轴(如跟随[-sibling] 或前置[-sibling])都可能具有二次性能,并且通常谓词[last()] 不会让它变得更好。您还需要考虑是否会对文档顺序进行排序 - 使用 -explain 查看执行计划可能会有所启发。
    • 可能取决于数据的结构,对吧?我想我可以假设查询 (following-sibling::foo/descendant::bar)[1] 的线性性能,因为我知道 1. 第一个具有匹配后代的 following-sibling 最多,比如说,5 个节点(与总数据集是),2.每个查询的后代轴的大小大致恒定(同样,与总数据集的大小无关)。
    猜你喜欢
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 1970-01-01
    相关资源
    最近更新 更多