Sorting Results

When results are generated from a query, content is generated in the same order as the results. For an XML datasource, generated content will be output in the order corresponding to the results of the XPath query expression. For an Sqlite datasource, the generated content corresponds to the order of results of the SQL query. For an RDF datasource, again, the results are in the order the query generates them, although except in the case of an RDF Seq, this order is arbitrary as RDF triples don't occur in any particular order. When the reference node is an RDF Seq container and the results are in that container, however, the template builder orders the results in the order the items appear in the Seq. For instance, the photos are listed in the same order in this example as they appear in the Seq in the datasource.

This method of sorting a Seq works best for the simple query syntax since it is obvious how the starting ref relates to the end member results (they are just the children), or for extended syntax queries that follow a similar pattern. For more complex queries, this natural sorting will not work, because the sort service assumes that the starting ref resource is the container and the end results are the children. In this case, the natural order of the results will just be the order that the template builder generates the results.

For ascending or descending sorts, this doesn't matter, since it will ignore whether results are containers and just sort by a value, alphabetically or numerically depending on the type of data.

When using a tree, sorting may be performed on each column of the tree. For instance, if a list of photos was displayed in a two column tree showing the title and description, you could sort by either title or description. The user can change the sort column and direction by clicking the column headers, however, you can programmatically change the sort as well.

Sorting Tree Results

We'll examine sorting of trees first since trees are the most common element used with sorted items. Using a tree builder, you can sort the results in a tree by a column. To do this, place a sort attribute on a treecol element referring to the variable to sort by for that column.

<treecol id="name" label="Name" sort="?name" flex="1"/>
<treecol id="date" label="Date" sort="?date" flex="1"/>

In this example, the first column will be sorted by the ?name variable and the second column by the ?date variable. When the sort is ascending, the tree rows will be sorted in alphabetical order. When the sort is descending, the tree rows will be sorted in the reverse order. For natural sorting, the rows will be sorted according to the natural order. Only one column applies a sort at a time. If the tree is sorted by name, and the user clicks on the date column header, the sort will change to the date column.

There are two additional attributes used for sorting, which you may set on a column to specify the initial sort. These attributes are modified when the user changes the sort. The sortDirection attribute may be used to specify the initial sort direction for a column. Only one column should have this attribute set, as a tree may only be sorted by one column at a time. The value should be either 'ascending', 'descending' or 'natural'. This last value is the default if the attribute is not specified. The sortActive attribute may be set to true or false and specifies which column the tree is sorted by. Only one column should have the sortActive attribute set to true at a time. The tree will change both attributes as necessary automatically when the column headers are clicked or the tree is sorted by other means.

If you don't want to allow sorting by a certain column, you can leave out the sort attribute. Only specify this attribute on columns that you wish to allow the user to sort by.

Here is a complete example of sorting a tree.

The sort attribute should be set to the variable that holds the values to sort by. Usually, this would be the same variable that is used to generate the label for the cells in that column, however this is not actually necessary. For instance, in the example the second column sorts by date, but if you were to use a different variable such as ?description, the tree would sort by the value of the description variable for each row. In almost all situations however, you would normally sort using the same variable used for the label value. However, one situation where this is not desirable is if the displayed values would not generate the correct order as there is a different representation that is more accurate. For example, the date 'May 15' would appear after 'August 24' when sorted purely alphabetically but before it when sorted chronologically.

For RDF datasources, another way to sort by dates is to use the the parseType="Date" construct in the RDF datasource. This marks a literal as being a date value rather than a string. The builder will recognize this and sort chronologically instead. This also has the advantage that the dates will be displayed according to the user's current locale (meaning that the date is formatted so as to be suitable for the user's language). Here is a sample of how to specify this in the RDF/XML datasource:

<rdf:RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:r="http://www.xulplanet.com/rdf/"
     xmlns:nc="http://home.netscape.com/NC-rdf#">
  <rdf:Description rdf:about="http://www.xulplanet.com/ndeakin/images/t/palace.jpg">
    <r:date nc:parseType="Date">1125966767295<r:date>
  </rdf:Description>
</rdf:RDF>

You can also specify parseType="Integer" for numbers which will allow sorting numerically. By specifing different types for different values, you can sort alphabetically, numerically or by date.

If you are using the simple RDF query syntax, there are no variables, so you need to specify the full predicate including the rdf: prefix in the sort attribute. For instance:

<treecol id="name" label="Name" sort="rdf:http://purl.org/dc/elements/1.1/title" flex="1"/>

Note that all of this discussion about sorting only applies to tree builders. For other elements or content trees, sorting is done by placing the attributes on the root element.

Content Sorting

For content builders (templates that do not use flags="dont-build-content"), sorting is done by placing the sort and sortDirection attributes on the root node of the template. The sortActive attribute is not needed. In this example, the list of people is sorted ascending by name.

<listbox datasources="people2.xml" ref="*" querytype="xml"
         sort="?name" sortDirection="ascending">
  <template>
    <query expr="*/*"/>
    <action>
      <listitem uri="?" label="?name"/>
    </action>
  </template>
</listbox>

It will often be the case that multiple items have the same value, especially for larger amounts of data. You can specify multiple sort keys to further define how a sort should be handled in the case where the values for the first sort key are the same. To do this, just add another key after the first separated by a space, as in the following:

sort="?name ?gender"

In this example, results will be sorted by name, followed by gender for those with identical names. You may add as many sort keys as desired.

Sorting occurs when output is first generated as well as when a result is added as a result of a datasource change. When the RDF datasource changes, and a new result is available, the new content is inserted at the right location.

For RDF datasources, you can also use an RDF predicate for sorting instead of a variable:

<hbox datasources="template-guide-photos5.rdf"
      sort="http://purl.org/dc/elements/1.1/title"
      sortDirection="ascending"
      ref="http://www.xulplanet.com/rdf/myphotos">
  <template>
    <vbox class="box-padded" uri="rdf:*">
      <image src="rdf:*"/>
      <label value="rdf:http://purl.org/dc/elements/1.1/title"/>
    </vbox>
  </template>
</hbox>

In this example, the generated items will be sorted by title. When sorting, the predicate specified in the sort attribute for the result is used to determine where in the content the generated output should be inserted. The is similar to using a variable but any predicate may be used, not just one that is used in the template.

Changing the Sort

To change the sorting for a template's generated contents, you can either change the sort related attributes and rebuild the template, or you can call the sort method of the sort service:

var listbox = document.getElementById("aListBox");

var sortService = Components.classes["@mozilla.org/xul/xul-sort-service;1"].
                    getService(Components.interfaces.nsIXULSortService);
sortService.sort(listbox, "?gender", "descending");

This code will sort a listbox by title in a descending order. The arguments to the sort method specify the root node (the listbox), the sort key and the sort direction.