correctly sorting and pagination XSL

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
help-is-neededAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Gertone (Geert Bormans)Information ArchitectCommented:
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
help-is-neededAuthor Commented:
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

Gertone (Geert Bormans)Information ArchitectCommented:
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>

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
PMI ACP® Project Management

Prepare for the PMI Agile Certified Practitioner (PMI-ACP)® exam, which formally recognizes your knowledge of agile principles and your skill with agile techniques.

help-is-neededAuthor Commented:
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
Gertone (Geert Bormans)Information ArchitectCommented:
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
help-is-neededAuthor Commented:
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
Gertone (Geert Bormans)Information ArchitectCommented:
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
help-is-neededAuthor Commented:
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
Gertone (Geert Bormans)Information ArchitectCommented:
for last record you need to count the filtered records, not all records
help-is-neededAuthor Commented:
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.
Gertone (Geert Bormans)Information ArchitectCommented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
XML

From novice to tech pro — start learning today.