Re: [xsl] Constraint Searching in XML via XSLT

Subject: Re: [xsl] Constraint Searching in XML via XSLT
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Fri, 20 Jul 2001 15:43:43 +0100
Hi Joel,

> I am using XML as a kind of client-side database for the project I'm
> currently doing. I have an SQL database on the server side which
> generates XML data files, and also XML an XML index file. The XML
> index file contains an ID for each data file (in this case a Trial),
> and a whole lot of attributes. I want to transform into an HTML
> Search Result. I want to do this by setting top-level parameters in
> the XSLT stylesheet (these parameters are to be the values for the
> constraints).

It would probably be most efficient, if you can arrange it, to have
the attributes specified in the SQL query that you use to get the data
out of your database rather than retrieving all of it and then using
XSLT to filter it.

However, it might be that you can't do that. To answer your specific
questions:

> 1. If a constraint is not specified, how do I get the XSLT
> stylesheet to match all elements?

I'd suggest that you change from applying templates to all the TrialID
elements and only matching some of them to applying templates to only
those TrialID elements that you're actually interested in. For
example, you can apply templates to only those TrialID elements that
have a TrialName equal to $trialName with:

  <xsl:apply-templates
    select="vfdindex:TrialID[vfdindex:TrialName = $trialName]" />

Your template giving the details of the relevant TrialID elements can
then match *all* TrialID elements - it just won't be applied to those
TrialID elements that don't match because they're not selected to have
templates applied to them.

To deal with unspecified constraints, you need to choose a default
value for the constraint that can never be a real value for that
constraint. For some, that might be an empty string (''), for others
it might be NaN or 'myUniqueUnspecifiedConstraintValue'. It depends on
what data could be held by the values. Usually an empty string will
do.

If you want to process all TrialID elements only when all the
constraints are unspecified, then you need something like:

  <xsl:apply-templates
    select="vfdindex:TrialID
              [($trialName = '' or vfdindex:TrialName = $trialName) and
               ($rainfall = NaN or vfdindex:Rainfall = $rainfall) and
               ($soilType = 'unspecifiedSoilType' or
                vfdindex:SoilType = $soilType)
               ...]" />

If you want to include partial matches, then you need to test whether
all the constraints are unspecified explicitly:

  <xsl:choose>
    <xsl:when test="$trialName = '' and
                    $rainfall = NaN and
                    $soilType = 'unspecifiedSoilType' and
                    ...">
      <xsl:apply-templates select="vfdindex:TrialID" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates
        select="vfdindex:TrialID
                  [vfdindex:TrialName = $trialName or
                   vfdindex:Rainfall = $rainfall or
                   vfdindex:SoilType = $soilType or
                   ...]" />
    </xsl:otherwise>
  </xsl:choose>
               
> 2. In the case of rainfall, I want to allow users to specify a
> rainfall *range*. Is it possible to do this in XSLT? I had a look
> and there seem to be < and > operators.

You're right that there are < and > operators. If you have a maximum
and minimum rainfall held in different variables (e.g. $maxRainfall,
$minRainfall) then you can test whether the vfdindex:Rainfall element
has a value between those values (inclusive) with:

  vfdindex:Rainfall >= $minRainfall and
  vfdindex:Rainfall &lt;= $maxRainfall

You have to use &lt; to escape the < because you're writing XML.

> 3. Should I be using <xsl:choose> and <xsl:if> instead of
> <xsl:template> for this sort of stuff?

Use what you feel comfortable with. You could apply templates to all
the TrialID elements and then do tests within the template to see
whether they match the parameters instead:

<xsl:template match="vfdindex:TrialID">
  <xsl:if test="vfdindex:TrainName = $trialName or
                vfdindex:Rainfall = $rainfall or
                ...">
    ...
  </xsl:if>
</xsl:template>

Only applying templates to the TrialID elements that you're interested
in may be slightly more efficient, I think (?), and has the advantage
that if you create a node set of those TrialID elements then you can
apply templates to that set multiple times in different modes to
create summary tables etc. without testing each TrialID element
multiple times.

I hope that helps,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread