Tuesday, 20 February 2007
XSLT Best practices: ancestor-or-self vs. descendants-or-self
I’ve been following a thread on our forum, XSL processing correctly in preview mode but not on the live website (requires registration), where a question was raised on how to find a landing page through XSLT. We had this XML scenario:
Sitecore
Content
Home [@template = ‘homeitem’]
Section A [@template=’section]
Document 1 [@template=’document’]
Document 2 [@template=’document’]
Section B [@template=’section]
Document 3 [@template=’document’]
Document 4 [@template=’document’]
Document 5 [@template=’document’]
Document 6 [@template=’document’]
Document 7 [@template=’document’]
The challenge was to find the section item depending on the current node. For example, if my current node was Document 5, the question was how to find the item based on template type section which was residing in the branch. In this example, it should have returned Section B.
The developer was almost there with this XPath:
<xsl:variable name=”home”
select=”/*/item[@key=’content’]/item[@key=’home’]” />
<xsl:variable name=”sectionitem”
select=”$home/item[descendant-or-self::item=current()]” />
This method seemed to work fine in some instances, but failed sometimes. Why was this?
We soon figured out it was the node comparison that failed: Comparing item=current() matches the entire node structure of these nodes, and even though the above data structure looks simple, the real data structure contains several nodes and attributes per item (e.g. versions, internal references and whatever makes up a Sitecore item xml).
Another problem is this comparison can be extremely slow, depending on the size of the data structure: When comparing node B with the current node, these entire structures, with items, children and attributes is compared.
Instead of using this approach, comparison should match for attributes:
<xsl:variable name=”sectionitem”
select=”$home/descendent-or-self::item[@id = current()/@id]” />
In the above example, only the attribute of the item is matched. I would estimate the match to be somewhere around 10-1000 times faster, naturally depending on the size of the data structure.
Now, another developer on the forum, Ben Golden gave another observation on optimization:
By using the above (well functioning, faster, but still slow) XPath, we would scan the entire data structure (match this item, then descendants, to find the top section node), in this case matching 9 items). Why not use current position (currentitem) to find the matching ancestor, instead of using the home node and matching on all underlying items?
E.g.something like:
<xsl:variable name="sectionitem" select="ancestor-or-self::item[@template='landingpage']" />
Naturally Ben is right. When current node would be Document 5, we would only match for 6 items. And with a larger structure, this number of comparisons would not increase while the descendants approach would.
Lessons learned:
- Always match on attributes!
- Always try to find an ancestor-of-self alternative for descendants-or-self when searching for a specific node!
19:32 Posted in Sitecore | Permalink | Comments (0) | Email this | Tags: Sitecore, XSLT



The comments are closed.