Quirkiness accessing the site root item from a descendant using Sitecore Query

Sitecore Query, with its XPath-like query syntax, has its share of quirks. It doesn’t always quite return the expected results, and one can’t use all functions available in later versions of XPath, either (though it does have some very useful Sitecore-specific functions). One can tackle these issues by extending Sitecore Query to include desired functionality. However, depending on the context this may not work well. Swapping out the built-in expression evaluation would be non-trivial (though useful, for example to merge Sitecore Query functionality with non-Sitecore search while retaining a declarative programming style, integrating with an ORM such as the Glass Mapper, or making non-Sitecore-Query results available everywhere in Sitecore itself that Sitecore Query is used, a topic for another exploration).

Everyone who’s used Sitecore extensively has had to access the site root item. This is not tough programmatically using the Sitecore API, and often done using extension methods. But when it’s helpful to use Sitecore Query, especially where the context item is at an undetermined depth in the content tree, one can run up against limitations of Sitecore Query.

I first discovered this on a project using Sitecore 7.2. The obvious approach is to use the ancestor axis in the query, but using index-based queries turned out to be funky; queries with an index of 1 would return the top three items (/sitecore, /sitecore/content, and the relevant child item of /sitecore/content). I found that using the following query would return the item at the specified level plus two, in this case actually returning the site-root item I was after:


Out of curiosity I determined that the following would also work, with varying degrees of hackiness:

./ancestor::*[@@key='home']/ancestor::*[@@key != 'sitecore' and @@key != 'content']
./ancestor::*[position() = 1 and @@key != 'sitecore' and @@key != 'content']

These are written for readability; one could use @@templateid instead of @@key with the template ID of the /sitecore/content item as well, or simply restrict to the template of the site home/root item itself, where that template is guaranteed to be unique in the hierarchy as it often is. The latter is what I wound up doing and it worked fine, but I came away with an enhanced respect for the sheer individuality of Sitecore Query.