[xsl] Can I use xsl:key to select elements up to certain ancestor

Subject: [xsl] Can I use xsl:key to select elements up to certain ancestor
From: "Michael Müller-Hillebrand mmh@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 30 Aug 2017 18:49:12 -0000
Hi all,

I had an embarrassing moment today, when I found that the source of a severe
performance problem was sitting in front of the screen.

Using XSLT 2.0 I am transforming elements and at many points I have to look up
the ancestor axis, but only up to an element with an attribute "id". Basically
I have a deeply nested structure of topics and I want to analyze ancestors
inside the current topic.

The nearest topic is easy to find:

<xsl:variable name="topic" select="ancestor-or-self::*[@id][1]"
as="element()"/>

And then I was doing the following to find all ancestors that share the same
"topic ancestor":

<xsl:variable name="ancestors-in-topic"
  select="ancestor-or-self::*[ancestor-or-self::* = $topic]"
as="element()*"/>

This did not what I expected and also wasted a lot of resources because the
predicate was true for all or most elements. After some testing I went with
this:

<xsl:variable name="ancestors-in-topic"
  select="ancestor-or-self::*[ancestor-or-self::*/generate-id() =
generate-id($topic)]" as="element()*"/>

Since that calculation is done very often I am wondering if xsl:key could be
used to speed things up?

Any pointers are very welcome, as always, thanks,

- Michael

PS: My sample XML and XSLT below.

<?xml version="1.0" encoding="UTF-8"?>
<bars id="x0">
  <bar id="x1">
    <bar id="x11">
      <bar id="x12">
        <div>
          <bar id="x13">
            <div>
              <p/>
            </div>
          </bar>
        </div>
      </bar>
    </bar>
  </bar>
</bars>


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:xs="http://www.w3.org/2001/XMLSchema"; version="2.0">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="p">
    <xsl:variable name="topic" select="ancestor-or-self::*[@id][1]"
as="element()"/>
    <xsl:variable name="ancestors-in-topic"
select="ancestor-or-self::*[ancestor-or-self::*/generate-id() =
generate-id($topic)]" as="element()*"/>
    <xsl:copy>
      <xsl:attribute name="topic" select="$topic/@id"/>
      <xsl:text>Ancestors: </xsl:text>
       <xsl:sequence select="$ancestors-in-topic/name()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="* | @*">
    <xsl:copy>
      <xsl:apply-templates select="@*, node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>


Expected output for <p>: <p topic="x13">Ancestors: bar div p</p>

Current Thread