Multiple Rules

All of the examples shown so far have used only a single rule. The builder supports the use of multiple rules as well. This involves using additional rule elements after the first. In addition, conditions may be applied to each rule. Only those results that match the conditions have content generated for them. For each result, the conditions associated with each rule are examined to see if they match. The first rule that matches causes content to be generated for that result. For example, the first rule might match directories or folders, whereas the second rule might match files. In this situtation, you would want to use different output for each type. If a result from the query doesn't match any of the rules, it is ignored and no content is generated for it.

Note that only the first rule, in the order that the rules appear in the template, that matches for a result is used, not all of them that match. Thus, you can assume that earlier rules handled earlier matches. For instance, if the first rule matched all folders, then the remaining rules can assume that any results that get past the first rule are not folders. This allows the conditions can be simpler. It also allows a common technique where the last rule in a template has no conditions, allowing all remaining results that didn't match earlier rules to match this last rule.

A multiple rule template looks like the following:

<hbox id="photosList" datasources="template-guide-photos3.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos">
  <template>
    <query>
      <content uri="?start"/>
      <member container="?start" child="?photo"/>
      <triple subject="?photo"
              predicate="http://purl.org/dc/elements/1.1/title"
              object="?title"/>
    </query>
    <rule>
      <where subject="?title" rel="equals" value="Canal"/>
      <action>
        <button uri="?photo" image="?photo" label="View" orient="vertical"/>
      </action>
    </rule>
    <rule>
      <action>
        <image uri="?photo" src="?photo"/>
      </action>
    </rule>
  </template>
</hbox>

Image:Template-guide-mult1.png

This template contains a query with two rules. The query generates a list of the photos and assigns the title of each photo to the variable '?title'. Note that the full query syntax must be used to use conditions within rules. The first rule contains a new element, where which is used to define a condition. The second rule does not contain a where element, so it matches results unconditionally, or any result that wasn't matched by a previous rule.

In this example, the where element in the first rule defines a condition which matches results that have a title of 'Canal'. Three attributes are used for this purpose.

The subject is the variable from the result that want to compare. In this case, '?title' is used to compare the title.

The rel specifies a relation operator to compare with. Here 'equals' is the operator used so an exact match is needed.

The value attribute is the value to compare to, which here is 'Canal'.

As the datasource has three photos, one of which has a title of 'Canal', this photo will match the first rule and the content's of that rule's action body will be generated. The remaining two photos do not match the first rule, however they do match the second rule, as the second rule has no where clauses. For these two photos, the action body of the second rule will apply.

When used together in this example, the results are combined and only three results are shown. However, you will probably notice that the one photo that matches the first rule has appeared differently that the others. In fact, the content for this photo is that of the first rule with the button, whereas the content for the other photos is that of the second rule with the normal images.

Here is another example for an XML source:

<radiogroup datasources="people.xml" ref="*" querytype="xml">
  <template>
    <query expr="person"/>
    <rule>
      <where subject="?gender" rel="equals" value="male"/>
      <action>
        <radio uri="?" label="?name is male"/>
      </action>
    </rule>
    <rule>
      <action>
        <radio uri="?" label="?name is female" disabled="true"/>
      </action>
    </rule>
  </template>
</radiogroup>

In this example, all male people are matched with the first rule, and a radio button is generated for each one. We assume that anyone that isn't male is female and use the second rule to create a disabled radio button for females. As all males have been filtered out by the first rule, we do not need any conditions for the second rule.

Image:Template-guide-mult2.png

Where Elements

We saw in the example above how to compare a value exactly to a specific value. This would be useful for comparing a field that identified the type of a result. However, there are many different ways in which the where element may be used.

Beside the equals operator, there are several other operators that may be used.

OperatorDescriptionExample
equalsMatch if a value equals anotherMatch a specific value
lessMatch if a number is less than anotherMatch only negative values with one rule, positive values with another
greaterMatch if a number is greater than anotherDisplay values greater than 1000 differently
beforeMatch if a value comes before another alphabetically
afterMatch if a value comes after another alphabetically
startswithMatch if a value starts with a specific string
endswithMatch if a value ends with a specific string
containsMatch if a value contains a substringTo highlight results that contain a search term

Here is an example using the 'contains' operator:

<vbox datasources="people.xml" ref="*" querytype="xml">
  <template>
    <query expr="person">
      <assign var="?letters" expr="string-length(@name) - 1"/>
    </query>
    <rule>
      <where subject="?name" rel="contains" value=" "/>
      <action>
        <label uri="?" value="?name has two names for a total length of ?letters"/>
      </action>
    </rule>
  </template>
</vbox>

This example contains only one rule with a condition which checks for names that contain a space character, which has the effect of selecting only those people with multiple names. People with only a single name, as 'Cleopatra' in this example, won't match any rule so won't have any content at all generated for it. This is an effective way to filter out results that you don't want. In this simple example with one rule though, it would likely be simpler and faster to just filter these out using the query XPath expression rather than generating the results then using a condition to filter them. Rules and conditions are more useful when using multiple rules.

We can add multiple where elements if we want to check additional conditions. When multiple conditions are present, they must all match for the rule to apply. Adding to the previous example, here we check for people with a space in their name, and a total number of letters less than 15. Note the assign element in the example above, used to calculate the '?letters' variable from the length of the name, minus one for the space character. Naturally, this won't work for people with more than two names (Joan of Arc for instance), but rule conditions allow a number of useful ways to filter results.

<rule>
  <where subject="?name" rel="contains" value=" "/>
  <where subject="?letters" rel="less" value="15"/>
  <action>
    <label uri="?" value="?name has two names for a total length of ?letters"/>
  </action>
</rule>

Negating a Condition

You can reverse a where clause by using the negate attribute. By setting the negate attribute to true, the condition matches only those that don't match the condition, reversing the results that match. For example, these two conditions have the opposite effect. The first matches only results with a ?title of 'Canal', and the second matches those that do not.

<where subject="?title" rel="equals" value="Canal"/>
<where subject="?title" rel="equals" value="Canal" negate="true"/>

You can use the negate attribute with all of the relational operators. For example, although there is no 'greater or equal' operator, you can acheive this effect with a negated 'less' operator.

<where subject="?age" rel="less" value="10" negate="true"/>

Additional Where Clause Attributes

By default, the case of values must match in a condition. You can disable this for a where clause by setting the ignorecase attribute to true. The following example matches names that contain either the letter 'a' or 'A'. Without the ignorecase attribute, names with only uppercase letter A's will not match the rule.

<where subject="?name" rel="contains" value="a" ignorecase="true"/>

Sometimes, you will want to check several different values in a single where clause. You can do this with the multiple attribute set to true.

<where subject="?title" rel="equals" value="Canal,Palace" multiple="true"/>

In this example, the rule will match if the title is equal to either 'Canal' or 'Palace'. Separate each value to check with a comma. As long as the variable matches one of the values, the where clause will match.

The multiple attribute may be used in combination with all of the operators, as well as the ignorecase and negate attributes. When using the negate attribute, the where clause will match if none of the values match.