Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Andrew Waegel 126 posts 126 karma points
    Oct 01, 2010 @ 03:17
    Andrew Waegel
    0

    Selecting specific nodes below a particular node

    I want to select all nodes with a particular property value that reside below a particular node in my content heirachy. After reading around I've actually got it working, but think there must be a more concise way to do it in xpath:

    <xsl:for-each select="$currentPage/ancestor-or-self::*[@isDoc and @level=1]/*[@isDoc and @nodeName='Resources']//*[@isDoc and contains(categories,$listingCategory)]">
    <!-- output stuff -->
    </xsl:for-each>      

    In this case, the node with the name 'Resources' is what I want to start the search, then I want to filter on the 'categories' property of all its child nodes, selecting only where that contains the substring in $listingCategory.

    Is there any more optimal way to do this? Seems like a lot of extra stuff in there.

    Thanks!
    - Andrew

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Oct 01, 2010 @ 10:15
    Chriztian Steinmeier
    0

     

    Hi Andrew,

    The best way to narrow down an XPath is to make sure you include as much information you already know about the document you're selecting from. It can also be a good idea to create a couple of extra variables along the way. So let's go:

    The first part grabs the root of the website (in an Umbraco install with multiple sites, the root of each is usually at level 1):

    $currentPage/ancestor-or-self::*[@isDoc and @level = 1]

     

    Here you can safely shave off the @isDoc predicate, because the ancestor axis of $currentPage can only contain document nodes.

    I usually put this in a variable called siteRoot:  

    <xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" />

     

    If you have a special DocumentType that's only used for the website home (I always have) you can just use that, which will be the most efficient:

    <xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::WebSite" />

     

    The next part finds the 'Resources' node - here you can also get rid of the @isDoc predicate, because it's very unlikely (impossible in the current version) that an Umbraco property would ever have an attribute named @nodeName:

    <xsl:variable name="resources" select="$siteRoot/*[@nodeName = 'Resources']" />

     

    Again - if you know the DocumentType, use that:

    <!-- A) Regular textpage: -->
    <xsl:variable name="resources" select="$siteRoot/Textpage[@nodeName = 'Resources']" />
    <!-- B) Special DocumentType: -->
    <xsl:variable name="resources" select="$siteRoot/ResourcesFolder" />

     

    So the last bit selects the documents you want to process, and since you're selecting based on the contents of a property, you probably also know which DocumentType that property sits on, right?

    The "//" (descendant axis) here is very important to only include if needed, because it looks at ALL nodes that are descendants of the 'Resources' node.

    That is, if you don't know if the documents can be children and/or grandchildren of the 'Resources' node you need it, but otherwise make sure to reduce it to what's actually needed. Indeed include the @isDoc predicate to at least only search Documents:

    <xsl:variable name="documents" select="$resources//*[@isDoc][contains(categories, $listingCategory)]" />

     

    <!-- With a known DocumentType: -->
    <xsl:variable name="documents" select="$resources//Presentation[contains(categories, $listingCategory)]" />

     

    Again: If you know the documents needed will only be children (not grandchildren) of the 'Resources' node, you can save valuable processor time by using "/" in stead of "//".

    So final result:

    <xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" />
    <xsl:variable name="resources" select="$siteRoot/*[@nodeName = 'Resources']" />
    <xsl:variable name="documents" select="$resources//*[@isDoc][contains(categories, $listingCategory)]" />
    
    <xsl:for-each select="$documents">
      <!-- output stuff -->
    </xsl:for-each>

     

    Hope that gave you some insight (and an urge to become an XPath Ninja! :-)   

    /Chriztian

     

     

     

     

     

     

  • Andrew Waegel 126 posts 126 karma points
    Oct 01, 2010 @ 20:07
    Andrew Waegel
    0

    That's extraodinarily helpful, thanks so much!

  • Shaun Smith 35 posts 61 karma points
    Jan 24, 2011 @ 03:00
    Shaun Smith
    0

    4 months later and you still helping people Chriztian, Thanks a lot!

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Jan 24, 2011 @ 09:22
    Chriztian Steinmeier
    0

    Hey Shaun,

    Good to hear - thanks :-)

    /Chriztian

  • Shaun Smith 35 posts 61 karma points
    Jan 24, 2011 @ 11:27
    Shaun Smith
    0

    Haha,

    Give a ninja a shuriken he can assasinate a doge, teach a ninja to throw and he can join crouching tiger hidden dragon!

    I love a good analogy >.<

Please Sign in or register to post replies

Write your reply to:

Draft