1
votes

What is the fastest way to find whether the current node is in path of a node with a specific attribute, given this xml structure:

<root>
<item>
    <item name="x">
        <item></item>
        <item name="y"></item>
        <item></item>
    </item>
    <item></item>
    <item></item>
</item>
<item>
    <item name="z"></item>
    <item></item>
</item>

I have a xslt variable to tell me what the current active node is.

I want to execute some code if the current node is in path of the node with the @name x.

So that if the active node is the item with @name y the code should execute, if the current active node is item with @name z it shouldn't.

My best solution so far is:

ancestor-or-self::item[@name = 'x']

But as there are 30k+ item nodes this is taking forever to render, is there a faster way? (XSLT/XPATH 2 is not an option)

2
You mean XSLT/XPATH version 2.0 is not an option, but XSLT/XPATH 1.0 is OK?khachik
@Hans Skov: The ancestor-or-self shouldn't be so heavy in performance. The problem must be in your transformation pattern: running this for every node should have a cost in performance.user357812
@Alejandro - I think he was saying that because he has 30k+ operations to perform the costs mount up, though I would suggest that's not going to change. ancestor-or-self is the correct solution I believe.annakata
@annakata: You could always avoid the checking. Check my answer.user357812
It would be helpful to see the full XSLT (or reduced example that shows where/how you are applying the XPATH). As @Alejandro points out, you may see better performance if your criteria are applied in the template @match, rather than buried within a template, because XSLT engines can apply optimizations more easily on the @match.Mads Hansen

2 Answers

2
votes

The best approach strongly depends on the whole transformation.

One could assume that pattern matching is optimized, so:

<xsl:template match="item[@name='x']//*">

Also, you could use modes like:

<xsl:template match="item[@name='x']">
    <xsl:apply-templates mode="descandants-in-path">
</xsl:template>

<xsl:template match="node()" mode="descandants-in-path">

Or a parameter like:

<xsl:template match="item[@name='x']">
    <xsl:apply-templates>
        <xsl:with-param name="pIsInPath" select="true()"/>
    </xsl:apply-templates>
</xsl:template>

<!-- Tunnel rule -->
<xsl:template match="*">
    <xsl:param name="pIsInPath"/>
    <xsl:apply-templates>
        <xsl:with-param name="pIsInPath" select="$pIsInPath"/>
    </xsl:apply-templates>
</xsl:template>
0
votes

To add to the good answer by @Alejandro:

In the name of completeness, in XSLT one could define a key:

<xsl:key name="kInSubtreebyId" 
         match="item[ancestor-or-self::item[@name='x']]"
         use="generate-id()"/>

And then reference this key like this:

<xsl:if test="key('kInSubtreebyId', generate-id())">
 . . . . . . .
</xsl:if>

In theory, the use of such key might be faster than other methods, if the test for any specific node is performed (in average) more than once.

However, you may need a lot of memory for the indices.