Re: [xsl] Improving efficiency of an XPath expression?

Subject: Re: [xsl] Improving efficiency of an XPath expression?
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Fri, 7 Jun 2002 13:50:27 +0100
Hi Michael,

> Now the challenge: given the id attribute value of any one <l>, find
> an XPath expression that will efficiently select a node-set
> containing only: the <lg> that immediately precedes the one where
> the <l> concerned is found, plus the <lg> that is the parent of that
> <l> and the <lg> that immediately follows the one that is the parent
> of the <l> specified. (i.e. "L2063" as input id would return
> precisely the three <lg> elements in the example.)

Assuming that there's only one l with a particular id, try something
like:

  /example/lg[l/@id = $targetid][1]/preceding-sibling::lg[1] |
  /example/lg[l/@id = $targetid][1] |
  /example/lg[l/@id = $targetid][1]/following-sibling::lg[1]

Or, if you can, store the lg that contains the relevant l element in a
variable:

  <xsl:variable name="lg" select="/example/lg[l/@id = $targetid][1]" />
  
  ... select="$lg/preceding-sibling::lg[1] |
              $lg |
              $lg/following-sibling::lg[1]" ...

In XPath 2.0, you'd be able to use a general step instead of a
variable, and still get the benefit of only finding the relevant lg
once:

  /example/lg[l/@id = $targetid][1]
    /(preceding-sibling::lg[1] | . | following-sibling::lg[1])

You could alternatively use something closer to what you had already:

  /example/lg[following-sibling::lg[1]/l/@id = $targetid or
              l/@id = $targetid or
              preceding-sibling::lg[1]/l/@id = $targetid]

but that would mean going through all the lg elements in the document
rather than stopping at the one that contains the relevant l element.

The important things for efficiency here are:

  - avoidance of the descendant axis -- step down the lg elements
    rather than using //

  - using [1], so that the processor only has to make one step rather
    than collecting *all* the lg elements and preceding/following
    siblings

If you're doing this multiple times in the same transformation, then
setting up a key so that you can quickly find the relevant lg by the
ids of its l elements would be a good idea:

<xsl:key name="lgs" match="lg"
         use="preceding-sibling::lg[1]/l/@id |
              l/@id |
              following-sibling::lg[1]/l/@id" />

and then just use:

  key('lgs', $targetid)

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread