?
Solved

correctly sorting and pagination XSL

Posted on 2011-10-06
11
Medium Priority
?
311 Views
Last Modified: 2012-05-12
Hi,

I've created an XSL that reads in XML data and transforms it successfully. I have introduced sorting which is working perfectly, then I added pagination which works but when they are both used together the results aren't exactly what I want.

Say I have 12 records that match the criteria of the xsl, If I limit the page size to 5 records and sort by price descending then it sorts the first page of results but doesn't necessarily put the most expensive of the 12 at the top, just the most expensive on that page of results. On the other 2 pages there could be more expensive ones.

How do I make this so that if I sort by price descending it sorts the records then paginates. so that the order is correct, and then the order is maintained as they scroll through the pages.

I can add my XSL if you would like to see what I am doing currently.

Thanks
0
Comment
Question by:help-is-needed
  • 6
  • 5
11 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36924177
That is likely because you are doing the paging first and after that order the page
(which is not an uncommon problem)

Note that if you would be using the preceding-sibling axis or following-sibling axis,
those operate on the elements in document order, not in sorted order

I suggested in this project that you would do the filtering and sorting in a first XSLT
and you would deal with the paging after that.
This suggestion had a reason... exactly this problem
Doing so would dramatically take down the amount of code you would need and would lessen the risk for errors

Are you still doing this in two steps? or did you abandon that approach.
I recommend still to make it two steps
0
 

Author Comment

by:help-is-needed
ID: 36924247
Hi Gertone,

Unfortunately due to the set up of the server and the content management system that is is due to run on it needs to be contained within the one XSLT. So I've had to abandon that.

Here is my code.. the transformation is done using some script on the server that feeds the variables and parameters in from the querystring.

Thanks
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" />
  <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:variable name="sf_price">Price</xsl:variable>
	<xsl:variable name="sf_rating">Rating</xsl:variable>
  <xsl:variable name="sf_resort_name">Resort</xsl:variable>
  <xsl:variable name="uparrow">up-arrow.jpg</xsl:variable>
  <xsl:variable name="downarrow">down-arrow.jpg</xsl:variable>
  <xsl:variable name="pound">£</xsl:variable>
  <xsl:variable name="uparrow-on">up-arrow-on.gif</xsl:variable>
  <xsl:variable name="downarrow-on">down-arrow-on.gif</xsl:variable>

  <xsl:variable name="DestToSelect">Byron Bay</xsl:variable>
  <xsl:variable name="sort">Price</xsl:variable>
  <xsl:variable name="sort_order">ascending</xsl:variable>
  <xsl:variable name="data_type">
    <xsl:choose>
      <xsl:when test="$sort='Price'">number</xsl:when>
      <xsl:when test="$sort='Product_Rating'">number</xsl:when>
      <xsl:otherwise>text</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:param name="first">1</xsl:param>
  <xsl:param name="size">5</xsl:param>
  
<xsl:template match="Root">
  <div>
    <xsl:call-template name="navigation"/>
  </div>
	<table style="background-color: #364750; color:#ffffff; width:960px; margin-top:5px" >
		<tr>
      <td width="600px"></td>
			<td width="40px"><xsl:value-of select="$sf_price"/></td>
			<td width="80px">
			  <xsl:choose>  
				<xsl:when test="$sort_order='ascending' and $sort='Price'">
				  <img alt="Sort ascending selected" src="{$downarrow-on}"/>
				  <a href="?sort=Price&amp;sort_order=descending">
					<img alt="Sort descending" src="{$uparrow}"/>
				  </a>
				</xsl:when>
				<xsl:when test="$sort_order='descending' and $sort='Price'">
				  <a href="?sort=Price&amp;sort_order=ascending">
					<img alt="Sort ascending" src="{$downarrow}"/>
				  </a>
				  <img alt="Sort descending selected" src="{$uparrow-on}"/>
				</xsl:when>
				<xsl:otherwise>
				  <a href="?sort=Price&amp;sort_order=ascending">
					<img alt="Sort ascending" src="{$uparrow}"/>
				  </a>
				  <a href="?sort=Price&amp;sort_order=descending">
					<img alt="Sort descending" src="{$downarrow}"/>
				  </a>
				</xsl:otherwise> 
			  </xsl:choose>
			</td>
			<td width="40px">
				<xsl:value-of select="$sf_rating"/>
			</td>
			<td>
				<xsl:choose>
					<xsl:when test="$sort_order='ascending' and $sort='Product_Rating'">
						<img alt="Sort ascending selected" src="{$downarrow-on}"/>
						<a href="?sort=Product_Rating&amp;sort_order=descending">
							<img alt="Sort descending" src="{$uparrow}"/>
						</a>
					</xsl:when>
					<xsl:when test="$sort_order='descending' and $sort='Product_Rating'">
						<a href="?sort=Product_Rating&amp;sort_order=ascending">
							<img alt="Sort ascending" src="{$downarrow}"/>
						</a>
						<img alt="Sort descending selected" src="{$uparrow-on}"/>
					</xsl:when>
					<xsl:otherwise>
						<a href="?sort=Product_Rating&amp;sort_order=ascending">
							<img alt="Sort ascending" src="{$downarrow}"/>
						</a>
						<a href="?sort=Product_Rating&amp;sort_order=descending">
							<img alt="Sort descending" src="{$uparrow}"/>
						</a>
					</xsl:otherwise>
				</xsl:choose>
			</td>
      <td width="40px">
        <xsl:value-of select="$sf_resort_name"/>
      </td>
      <td>
        <xsl:choose>
          <xsl:when test="$sort_order='ascending' and $sort='ServiceName'">
            <img alt="Sort ascending selected" src="{$downarrow-on}"/>
            <a href="?sort=ServiceName&amp;sort_order=descending">
              <img alt="Sort descending" src="{$uparrow}"/>
            </a>
          </xsl:when>
          <xsl:when test="$sort_order='descending' and $sort='ServiceName'">
            <a href="?sort=ServiceName&amp;sort_order=ascending">
              <img alt="Sort ascending" src="{$downarrow}"/>
            </a>
            <img alt="Sort descending selected" src="{$uparrow-on}"/>
          </xsl:when>
          <xsl:otherwise>
            <a href="?sort=ServiceName&amp;sort_order=ascending">
              <img alt="Sort ascending" src="{$downarrow}"/>
            </a>
            <a href="?sort=ServiceName&amp;sort_order=descending">
              <img alt="Sort descending" src="{$uparrow}"/>
            </a>
          </xsl:otherwise>
        </xsl:choose>
      </td>
		</tr>
	</table>

  <xsl:choose>
    <xsl:when test="$sort_order='ascending' and $sort='Price'">
      <xsl:for-each select="Product[position() >= $first and position() &lt; $first + $size and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="number" order="ascending" select="Story/Price"/>
        <xsl:call-template name="hotel-teaser" />
      </xsl:for-each>
    </xsl:when>
    <xsl:when test="$sort_order='descending' and $sort='Price'">
      <xsl:for-each select="Product[position() >= $first and position() &lt; $first + $size and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="number" order="descending" select="Story/Price"/>
        <xsl:call-template name="hotel-teaser" />
      </xsl:for-each>
    </xsl:when>
    <xsl:when test="$sort_order='ascending' and $sort='Product_Rating'">
      <xsl:for-each select="Product[position() >= $first and position() &lt; $first + $size and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="number" order="ascending" select="Story/Product_Rating"/>
        <xsl:call-template name="hotel-teaser" />
      </xsl:for-each>
    </xsl:when>
    <xsl:when test="$sort_order='descending' and $sort='Product_Rating'">
      <xsl:for-each select="Product[position() >= $first and position() &lt; $first + $size and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="number" order="descending" select="Story/Product_Rating"/>
        <xsl:call-template name="hotel-teaser" />
      </xsl:for-each>
    </xsl:when>
    <xsl:when test="$sort_order='ascending' and $sort='ServiceName'">
      <xsl:for-each select="Product[position() >= $first and position() &lt; $first + $size and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="text" order="ascending" select="Story/ServiceName"/>
        <xsl:call-template name="hotel-teaser" />
      </xsl:for-each>
    </xsl:when>
    <xsl:when test="$sort_order='descending' and $sort='ServiceName'">
      <xsl:for-each select="Product[position() >= $first and position() &lt; $first + $size and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="text" order="descending" select="Story/ServiceName"/>
        <xsl:call-template name="hotel-teaser" />
      </xsl:for-each>
    </xsl:when>
  </xsl:choose>
  
</xsl:template>

  <xsl:template name="hotel-teaser">
    <div id="TeaserBase" style="background-color: #364750; color:#ffffff; width:960px; margin-top:10px">
      <table id="teaser_content" style="padding:10px">
        <tr>
          <td>
            <div id="teaser_image" style="padding:10px 5px 10px 10px; width:430px">
              <xsl:element name="img">
                <xsl:attribute name="border">0</xsl:attribute>
                <xsl:attribute name="src">
                  <xsl:value-of select="Image_1/@href"/>
                </xsl:attribute>
                <xsl:attribute name="width">430</xsl:attribute>
                <xsl:attribute name="height">325</xsl:attribute>
                <xsl:attribute name="alt">
                  <xsl:value-of select="Story/ServiceName"/>
                </xsl:attribute>
                <xsl:attribute name="title">
                  <xsl:value-of select="Story/ServiceName"/>
                </xsl:attribute>
              </xsl:element>
            </div>
          </td>
          <td>
            <div id="teaser_right" style="padding:5px 5px 0 10px">
              <table>
                <tr>
                  <td colspan="2">
                    <div style="font-size:18px; font-weight:bold; max-width:480px">
                      <xsl:value-of select="translate(Story/ServiceName, $lowercase, $uppercase)"/>
                    </div>
                    <div>
                      <xsl:choose>
                        <xsl:when test="Story/Product_Rating = 1">
                          <img src="Star.png" alt="1" title="1" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 1.5">
                          <img src="Star1-5.png" alt="1.5" title="1.5" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 2">
                          <img src="Star2.png" alt="2" title="2" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 2.5">
                          <img src="Star2-5.png" alt="2.5" title="2.5" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 3">
                          <img src="Star3.png" alt="3" title="3" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 3.5">
                          <img src="Star3-5.png" alt="3.5" title="3.5" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 4">
                          <img src="Star4.png" alt="4" title="4" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 4.5">
                          <img src="Star4-5.png" alt="4.5" title="4.5" ></img>
                        </xsl:when>
                        <xsl:when test="Story/Product_Rating = 5">
                          <img src="Star5.png" alt="5" title="5" ></img>
                        </xsl:when>
                      </xsl:choose>
                      <xsl:text> </xsl:text>
                      <span style="font-size:15px; font-weight:bold">
                        <xsl:value-of select="translate(Story/Desc_Destinations, $lowercase, $uppercase)"></xsl:value-of>
                      </span>
                    </div>
                  </td>
                </tr>
                <tr>
                  <td>
                    <div id="teaser_text" style="width:290px; max-height:290px; height:290px">
                      <div style="width:280px; height:280px;">
                        <span style="font-size:12px;">
                          <xsl:value-of select="Story/Desc_Snapshot"/>
                        </span>
                      </div>
                    </div>
                  </td>
                  <td>
                    <table style="width:180px; height:290px">
                      <tr>
                        <td>
                          <div style="font-weight:bold">Hotel Facilities</div>
                        </td>
                      </tr>
                      <tr>
                        <td>
                          <div style="height:70px; overflow:hidden">
                            <xsl:value-of select="Story/Desc_Hotel_Facilities"/>
                          </div>
                        </td>
                      </tr>
                      <tr>
                        <td>
                          <div style="font-weight:bold; margin-top:5px;">Guest Rooms</div>
                        </td>
                      </tr>
                      <tr>
                        <td>
                          <div style="height:70px; overflow:hidden">
                            <xsl:value-of select="Story/Desc_Guest_Rooms"/>
                          </div>
                        </td>
                      </tr>
                      <tr>
                        <td>
                          <div style="font-weight:bold; margin-top:5px; font-size:14px">
                            From <xsl:value-of select="$pound"/> <xsl:value-of select="Story/Price" /> per person per night
                          </div>
                          <div style="font-size:smaller">
                            <xsl:value-of select="Story/Desc_Price_description"/>
                          </div>
                        </td>
                      </tr>
                      <tr>
                      </tr>
                      <td>
                        <table style="padding-top:10px;">
                          <tr>
                            <td>
                              <a href="#">
                                <img src="enquiry.gif" title="email us" alt="email us" border="0" />
                              </a>
                            </td>
                            <td>
                              <a href="#">
                                <img src="branch.gif" title="find a branch" alt="find a branch" border="0" />
                              </a>
                            </td>
                          </tr>
                        </table>
                      </td>
                    </table>
                  </td>
                </tr>
              </table>
            </div>
          </td>
        </tr>
      </table>
    </div>

  </xsl:template>


  <xsl:template name="navigation">
    <div>
      <a>
      <xsl:attribute name="href">
          <xsl:text>?first=</xsl:text>
          <xsl:value-of select="$first - $size"/>
          <xsl:text>&amp;size=</xsl:text>
          <xsl:value-of select="$size"/>
        </xsl:attribute>
        <xsl:text>&lt; previous</xsl:text>
      </a>
      <xsl:text>   </xsl:text>
      <xsl:for-each select="Product[position() mod $size = 1  and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:variable name="pos" select="position()"/>
        <xsl:variable name="this-start" select="1 + ($pos - 1) * $size"/>
        <xsl:choose>
          <xsl:when test="$this-start &lt;= $first and $first &lt; $this-start + $size">
            <span>
              <xsl:value-of select="$pos"/>
            </span>
          </xsl:when>
          <xsl:otherwise>
            <a>
              <xsl:attribute name="href">
                <xsl:text>?first=</xsl:text>
                <xsl:value-of select="$this-start"/>
                <xsl:text>&amp;size=</xsl:text>
                <xsl:value-of select="$size"/>
              </xsl:attribute>
              <xsl:value-of select="$pos"/>
            </a>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:text>   </xsl:text>
      </xsl:for-each>
      <a>
        <xsl:attribute name="href">
          <xsl:text>?first=</xsl:text>
          <xsl:value-of select="$first + $size"/>
          <xsl:text>&amp;size=</xsl:text>
          <xsl:value-of select="$size"/>
        </xsl:attribute>
        <xsl:text>next &gt;</xsl:text>
      </a>
    </div>
  </xsl:template>
</xsl:stylesheet>

Open in new window

0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 2000 total points
ID: 36924342
OK, we have been to this point before :-)

The issue is in the use of position().
That works on the entire set

     <xsl:for-each select="Product[position() >= $first and position() &lt; $first + $size and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="number" order="ascending" select="Story/Price"/>
        <xsl:call-template name="hotel-teaser" />
      </xsl:for-each>

I think it would work if you did do this like this

     <xsl:for-each select="Product[(translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">
        <xsl:sort data-type="number" order="ascending" select="Story/Price"/>
<xsl:if test="position() >= $first and position() &lt; $first + $size">
        <xsl:call-template name="hotel-teaser" />
</xsl:if>

If you would be able to use a nodeset, it would be easier
(but then you need to tell me which XSLT processor you are using)
      </xsl:for-each>

0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Author Comment

by:help-is-needed
ID: 36924787
Hi Gertone,

Thanks, i'll try replacing that code.
Will I also need to replace the pagination links that are created in the navigation template?

Thanks
0
 
LVL 60

Assisted Solution

by:Geert Bormans
Geert Bormans earned 2000 total points
ID: 36924867
Well, it strikes me that you don't have the filtering added correctly to the navigation, so it is wrong as it currently is anyway

      <xsl:for-each select="Product[position() mod $size = 1  and (translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))]">

should be
      <xsl:for-each select="Product[(translate(normalize-space(Story/Desc_Destinations), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn') = translate(normalize-space($DestToSelect), 'AZERTYUIOPQSDFGHJKLMWXCVBN', 'azertyuiopqsdfghjklmwxcvbn'))][position() mod $size = 1]">
I guess

Still, all would be much easier if you emulated a two step in one stylesheet
You can do so if you can use node-set extensibility one way or another.
But that is processor depending,
so again, if you can figure out what the processor is, you can improve this code dramatically
If you don't do that for now, do that for later maintenance
0
 

Author Comment

by:help-is-needed
ID: 36925143
Hi Gertone,

Im just asking the server guys now to see if any of them know for me, thanks.

Will I need to amend the way I build the previous and next pagination buttons also?

Thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36925661
previous and next only depend on the start point, so that would be OK, I guess



In case the server guys don't know

sneak this in your XSLT
<xsl:value-of select="system-property('xsl:version')" />//
<xsl:value-of select="system-property('xsl:vendor')" />//
<xsl:value-of select="system-property('xsl:vendor-url')" />

and you will find the information in the result
0
 

Author Comment

by:help-is-needed
ID: 36930134
Ok, i'll pop that in there today and upload it and let you know what comes back.

My final pagination questions is, how do I stop the next > link appearing if there are no more pages to scroll through.

I have it working for < prev as I have done a choose statement that checks to see if $first = 1, but not sure how to do it when it gets to the last record.

I'll open a new thread with the xsl version so you can get these points for the help you have already provided.

Thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36930398
for last record you need to count the filtered records, not all records
0
 

Author Comment

by:help-is-needed
ID: 36931552
Thanks, got it.
Also, here is what I got back from the XSLT processor code:
1 // James Clark // http://www.jclark.com/

Open source processor by James Clark.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36932292
well, that is a very old processor ("XT" it is)
I have never seen it used in production before.

It is the old processor James Clark wrote to test the XSLT specification (he is the author of version 1)
I never realised that people would use it in production

But I just checked, it supports xt:node-set, so you will get away with a two step
I have to go now.
I will download XT later (it must be the single one processor I don't have on my machine :-) and make a test example
for an XSLT twostep
that will likely be after the weekend
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The Problem How to write an Xquery that works like a SQL outer join, providing placeholders for absent data on the outer side?  I give a bit more background at the end. The situation expressed as relational data Let’s work through this.  I’ve …
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
this video summaries big data hadoop online training demo (http://onlineitguru.com/big-data-hadoop-online-training-placement.html) , and covers basics in big data hadoop .
This video shows how to quickly and easily deploy an email signature for all users in Office 365 and prevent it from being added to replies and forwards. (the resulting signature is applied on the server level in Exchange Online) The email signat…
Suggested Courses

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question