devilray
devilray

Reputation: 3

Efficient Random Nodes in XSLT

I need some advice, not being a guru at XSLT...

I have the following code, but the process is slow and results in the page load time being way too long! I know there must be another way, but I can't get my head around it.

Basically, I have a large list of nodes that I'd like to return 3 randomly, without it having to constantly 'for-each' through the entire list every time??

Any help would be greatly appreciated! Thanks.

<msxml:script implements-prefix="random" language="C#">
<msxml:assembly name="umbraco"/>
<msxml:using namespace="umbraco"/>
<![CDATA[
public int GetRandom(int minValue, int maxValue)
{
return umbraco.library.GetRandom().Next(minValue, maxValue);
}
]]>
</msxml:script>

 <xsl:template match="/">
   <xsl:variable name="bigCount" select="count(umbraco.library:GetXmlNodeById(1070)/descendant::* [@isDoc and string(umbracoNaviHide) != '1'][(self::AddANewsStory or self::AddAFeature or self::AddABlogEntry)])" />
   <xsl:for-each select="umbraco.library:GetXmlNodeById(1070)//descendant::*[@isDoc and string(umbracoNaviHide) != '1'][(self::AddANewsStory or self::AddAFeature or self::AddABlogEntry)]">
     <xsl:sort select="random:GetRandom(1, $bigCount)" order="ascending"/>
        <xsl:if test="position() &lt;= $repeated">
            <div class="relative hover-shadow mb">
              <a href="{umbraco.library:NiceUrl(@id)}"><img src="/ImageGen.ashx?image={concat(substring-before(umbracoFile ,'.'),'_Article Image.jpg')}" width="100%" alt="{newsTitle}" /></a>
              <div class="r_content">
                <xsl:variable name="catID" select="umbraco.library:GetXmlNodeById(selectACategory)/@id"/>
                <div class="r_title"><a href="{umbraco.library:NiceUrl(@id)}"><xsl:value-of select="newsTitle"/><xsl:value-of select="blogTitle"/><xsl:value-of select="featureTitle"/></a></div>
              </div>
            </div>
        </xsl:if>
 </xsl:for-each>
 </xsl:template>

Upvotes: 0

Views: 746

Answers (1)

Astuanax
Astuanax

Reputation: 667

I am not sure whether the random script call is what makes it slow, I think it is rather the random number generator in the xsl:sort call that messes up everything.

Try the below script:

Set a variable as a source. Don't use .net calls, they will only slow things down.

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

Gather your nodes.

<xsl:variable name="nodes">
 <xsl:apply-templates select="msxml:node-set($source)//*[@isDoc and string(umbracoNaviHide) != '1'][(self::AddANewsStory or self::AddAFeature or self::AddABlogEntry)]" mode=filter"/>
</xsl:template>
</xsl:variable>

Get the count from those nodes.

<xsl:variable name="bigCount" selet="count(msxml:nodeset($nodes)/*)"/>

Identity template, adding a random value as an attribute. Using the mode attribute to target specific templates: filter.

  <xsl:template match="*" mode="filter">    
       <xsl:attribute name="randomOrder">
        <xsl:value-of select="random:GetRandom(1,$bigCount)"/>
       </xsl:attribute>
       <xsl:copy-of select="node()|@*"/>
    </xsl:template>

Render the nodes. Using the mode attribute to target specific templates: render. Sort based on the new attribute randomOrder.

<xsl:template match="/">
  <xsl:apply-templates select="msxml:node-set($nodes)/*" mode="render">
     <xsl:sort select="@randomOrder" data-type="number" order="ascending"/>
  </xsl:apply-templates>
</xsl:template>

Item template to be rendered for each node.

<xsl:template match="*" mode="render">
 <div class="relative hover-shadow mb">
  <xsl:value-of select="."/>
 </div>
</xsl:template>

And the complete script with the correct namespace added: xmlns:random="urn:my-scripts"

   <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE xsl:stylesheet>
        <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:msxml="urn:schemas-microsoft-com:xslt" 
     xmlns:umbraco.library="urn:umbraco.library" 
     xmlns:random="urn:my-scripts" version="1.0" 
     exclude-result-prefixes="random msxml umbraco.library">

    <msxml:script implements-prefix="random" language="C#">
    <msxml:assembly name="umbraco"/>
    <msxml:using namespace="umbraco"/>
    <![CDATA[
    public int GetRandom(int minValue, int maxValue)
    {
    return umbraco.library.GetRandom().Next(minValue, maxValue);
    }
    ]]>
    </msxml:script>
         <xsl:param name="currentPage"/>
    <xsl:variable name="source" select="$currentPage/ancestor-or-self::*[@level = 1]//*[@isDoc][@id = 1070]"/>
            <xsl:variable name="nodes">
                <xsl:apply-templates select="msxml:node-set($source)//*[@isDoc and string(umbracoNaviHide) != '1'][(self::AddANewsStory or self::AddAFeature or self::AddABlogEntry)]" mode="filter"/>
            </xsl:variable>
            <xsl:variable name="bigCount" select="count(msxml:node-set($nodes)/*)"/>
            <xsl:template match="*" mode="filter">
                <xsl:attribute name="randomOrder">
                    <xsl:value-of select="random:GetRandom(1,$bigCount)"/>
                </xsl:attribute>
                <xsl:copy-of select="node()|@*"/>
            </xsl:template>
            <xsl:template match="/">
                <xsl:apply-templates select="msxml:node-set($nodes)/*" mode="render">
                    <xsl:sort select="@randomOrder" data-type="number" order="ascending"/>
                </xsl:apply-templates>
            </xsl:template>
            <xsl:template match="*" mode="render">
                <div class="relative hover-shadow mb">
                    <xsl:value-of select="."/>
                </div>
            </xsl:template>
        </xsl:stylesheet>

Upvotes: 1

Related Questions