我可以更快地进行此xpath搜索吗?

时间:2011-08-25 01:36:52

标签: xml xpath php-5.2

的xml:

<root>
 <a auto='1'>
  <b>
   <c auto="1">
    <d auto="1"></d>
   </c>
  </b>
  <e auto="1">
   <f>
    <g auto="1"></g>
   </f>
  </e>
 </a>
</root>

工作是: 找到所有元素:
1,是上下文元素的后代 2,具有'自动'属性
3,最高级别(自我和上下文元素之间没有祖先自动属性

因此,如果上下文节点 a ,则应返回 c e

我在我的php课程中实现它:
$tempId='XDFAY69LA';
$this->setAttribute('tempId',$tempId);
$path=".//*[@auto and not(ancestor::*[@auto and ancestor::*[@tempId='$tempId']])]";
$ar=$this->getElementsByXPath($path);
$this->removeAttribute('tempId');

但是我发现查询很慢,也许是..,因为查询过于复杂?,有没有办法做得更好?

我正在写一个测试,请看看:


    <?php
    $xml='
      <root>
        <a auto="1" tempId="current">
          <b>
            <c auto="1">
              <d auto="1"></d>
            </c>
          </b>
          <e auto="1">
            <f>
              <g auto="1"></g>
            </f>
          </e>
        </a>
      </root> ';

    $doc=new DomDocument();
    $tempId='XDFAY69LA';
    $doc->loadXml($xml);
    $domxpath=new DOMXPath($doc);
    $a=$domxpath->query('a')->item(0);
    $path=".//*[@auto and not(ancestor::*[@auto and ancestor::*[@tempId='$tempId']])]";
    $start=microtime(true);
    for($n=0;$n<1000;$n++){ //run 1000 times
      $a->setAttribute('tempId',$tempId);
      $ar=$domxpath->query($path,$a);
      $a->removeAttribute('tempId');
      for($i=0;$i<$ar->length;$i++){
        $node=$ar->item($i);
        //echo $node->tagName . "\n";
      }
    }
    $cost=round(1000 * (microtime(true)-$start));
    echo "time cost: $cost";
    ?>

3 个答案:

答案 0 :(得分:2)

使用

.//*[@auto and $tempId = ancestor::*[@auto][1]/@tempId]

这将选择具有auto属性的所有后代元素(上下文节点),其第一个具有auto属性的祖先也具有tempId属性,其值与该属性相同上下文节点的tempId属性(后者存储在$tempId变量中)。

这里我们假设没有两个不同的元素具有相同的tempId属性值。

基于XSLT的快速验证

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="a">
   <xsl:variable name="tempId" select="@tempId"/>

     <xsl:copy-of select=
      ".//*[@auto and $tempId = ancestor::*[@auto][1]/@tempId]"/>
 </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时

<root>
    <a auto="1" tempId="current">
        <b>
            <c auto="1">
                <d auto="1"></d>
            </c>
        </b>
        <e auto="1">
            <f>
                <g auto="1"></g>
            </f>
        </e>
    </a>
</root>

生成了想要的正确结果(生成两个元素ce

<c auto="1">
   <d auto="1"/>
</c>
<e auto="1">
   <f>
      <g auto="1"/>
   </f>
</e>

仅在XPath表达式中无法改善性能,效率低下是由于必须使用// XPath伪运算符。

如果使用XSLT,可以使用密钥获得有效的解决方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kfirstDescendents" match="*[@auto]"
  use="generate-id(ancestor::*[@auto][1])"/>

 <xsl:template match="a">
     <xsl:copy-of select=
      "key('kfirstDescendents', generate-id())"/>
 </xsl:template>
</xsl:stylesheet>

此转换产生与第一个相同的结果,并且在具有许多具有auto属性的嵌套元素的文档上显着更快

如果绝对排除使用XSLT,可以使用哈希表实现与XSLT密钥相同的效果(抱歉,不知道PHP)。

答案 1 :(得分:1)

从XPath开始:

 .//*[@auto and not(ancestor::*[@auto and ancestor::*[@tempId='$tempId']])]

怎么样:

 .//*[@auto and not(ancestor::*[@auto][ancestor::*[@tempId='$tempId']])]

甚至,

 .//*[@auto and count(ancestor::*[@auto][ancestor::*[@tempId='$tempId']])=0]

答案 2 :(得分:0)

我的想法是简化它。

$path=".//*[@auto and not(ancestor::*[@auto and not(@tempId='$tempId'))]";

“祖先:: * [@ tempId = '$ tempId']”

“不(@tempId = '$ tempId')”


//编辑内容:淘汰详细