Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xml 带谓词的XPath:如何避免二次时间增加_Xml_Xpath - Fatal编程技术网

Xml 带谓词的XPath:如何避免二次时间增加

Xml 带谓词的XPath:如何避免二次时间增加,xml,xpath,Xml,Xpath,考虑以下简化的Adobe Premiere项目文件: <Project> <Sequences> <UID.1>a</UID.1> <UID.2>b</UID.2> <UID.3>c</UID.3> </Sequences> </Project> <Sequence ObjectUID="a"> ...

考虑以下简化的Adobe Premiere项目文件:

<Project>
    <Sequences>
        <UID.1>a</UID.1>
        <UID.2>b</UID.2>
        <UID.3>c</UID.3>
    </Sequences>
</Project>
<Sequence ObjectUID="a"> ... </Sequence>
<Sequence ObjectUID="b"> ... </Sequence>
<Sequence ObjectUID="c"> ... </Sequence>
<Sequence ObjectUID="x"> ... </Sequence>
问题是,对于一个非常大的XML文件(100MB),即使其中只有20个序列元素,以及同一级别的100000个其他同级,搜索也需要很多秒

如果我只收集内部查询的结果,即

/Project/Nodes/*[starts-with(local-name(),"UID.")]
然后迭代20个UID,通过它们的ObjectUID通过
/PremiereData/Sequence[@ObjectUID='a']
等方式获取每个序列,速度非常快

这对我来说意味着Xpath处理器并不像我所期望的那样,首先计算(和不变的)内部表达式一次,然后选择外部表达式的元素,而是首先获取所有序列,然后重新计算每个序列的内部谓词,这正是它速度如此之慢的原因


我说的对吗?我是否可以更改查询以避免出现这种情况,或者添加提示以告知表达式计算器谓词是不变的?

处理器可能正在重新计算谓词中的表达式,如果您首先选择UID元素值并绑定到变量,您将看到性能提升,然后在
序列
元素的谓词过滤器中使用该变量

let $uid := /PremiereData/Project/Sequences/*[starts-with(local-name(),'UID')] 
return /PremiereData/Sequence[@ObjectUID = $uid]

大多数XPath处理器不进行任何智能连接优化,它们只使用嵌套循环。Saxon EE是我所知道的唯一一个以您建议的方式优化连接的处理器


如果您使用的是XSLT,那么使用xsl:key是常见的解决方法,但是在纯XPath中没有简单的答案——特别是在XPath 1.0中。

从技术上讲,性能是二次的,而不是指数的。二次曲线是1,4,9,16,25,36,49,64。。。而指数则是1,2,4,8,16,32,64,128。这似乎是一个迂腐的区别,但事实上,这是一个有用的诊断,知道数字遵循的模式。我考虑过这一点,甚至最初写了“二次或指数”,因为我无法理解它。我已经更新了标题。请注意,“let$x:=”仅在3.0版以后的XPath中可用。这可能是提供解决方案的最佳答案,尽管我的XML工具包显然还不支持v3。
let $uid := /PremiereData/Project/Sequences/*[starts-with(local-name(),'UID')] 
return /PremiereData/Sequence[@ObjectUID = $uid]