Skip to main content

The code always knows .. how to handle Query

Posted by jive on February 21, 2007 at 10:40 AM PST




http-equiv="content-type">
The code knows ... how to handle Query


One of the questions that came up in yesterdays debug hunt was this ...
"what to do when the query does not match the data". Lets see what the code has to say.



We have a couple of options on the table, and actually all of them are
implemented! While this is horrible for client code (they cannot know
what to expect) it is great for us since we will have working tested
code snipets to choose from when we decide what behavior is woth
keeping.



To review this problem (pretending we are using Query to
access a database) the client code has asked for all the
"Rivers" to be returned, but by mistake they asked the "Roads" table.



What to do?

Ignore the Problem

Always an option, sometimes this is phrased as "do what I mean not what
I say", by convention code is supposed to LOG a warning when this
occurs.



So when the user asked all the "rivers" in the "roads" table
...  we should actually change their query to "roads".



Here is what that looks like (from yesterdays debugging session):

cellpadding="2" cellspacing="2">
public DefaultFeatureResults(FeatureSource source, Query query) {
    this.featureSource = source;       
    FeatureType origionalType = source.getSchema();
   
    String typeName = origionalType.getTypeName();       
    if( typeName.equals( query.getTypeName() ) ){
        this.query = query;
    }
    else {
        this.query = new DefaultQuery(query);
        ((DefaultQuery) this.query).setTypeName(typeName);
    }
    ... handle query
}



I hate things that fail siliently in the night (geotools policy is to
produce a warning ... it just has not followed here yet).

Exceptionally Annoying

The next option is to throw an exception ... this is a popular approach
(implementors no longer have to worry about the problem, it is visible
to client code, and if the exception message is good developers can
figure out what went wrong and try again).



This is however an approach we are trying to move away from ... for two
reasons:

  • Failure during rendering sucks; you get an empty screen.
    Much nicer to see the data that worked ... and then sift through the
    LOG messages to discover why your rivers did not show up.
  • Failure during long running process hurts; GIS data is
    large usually you will need to try
    • Something quick and simple for 80% of the work
    • Break out something harder for 15%
    • 4% for data cleaning. Perhaps it was collected wrong
      (perhaps the lines are invalid .. or were made so by loss of percision)
    • 1%... and then you are down to a manual process

This approach is taken by the WFSFeatureSource:

cellpadding="2" cellspacing="2">
public FeatureCollection getFeatures(Query request) throws IOException {
    String typeName = featureType.getTypeName();

    if ((request.getTypeName() != null) && !typeName.equals(request.getTypeName())) {
        throw new IOException("Cannot query " + typeName + " with:" + request);
    }
    if (request.getTypeName() == null) {
        request = new DefaultQuery(request);
        ((DefaultQuery) request).setTypeName(featureType.getTypeName());
    }
    return new JDBCFeatureCollection(this, request);
}

Or be correct

The last option, and the one suggested by Andrea is to do what the user
said ... if they asked for all the rivers in the roads table give it to
them! The collection will however be empty.



This is easy enough to arrange .. from the final AbstractFeatureSource:

cellpadding="2" cellspacing="2">
public FeatureCollection getFeatures(Query query) throws IOException {
    FeatureType schema = getSchema();       
    String typeName = schema.getTypeName();       
       
    if( !typeName.equals( query.getTypeName() ) ){
        return new EmptyFeatureCollection( schema );
    }
    else {
        return new DefaultFeatureResults(this, query);   
    }       
}



I like it.

How Query is Handled

Here is the final result .. I have stolen code from all three examples
to cover such matters as a *null* typeName.

cellpadding="2" cellspacing="2">
public FeatureCollection getFeatures(Query query) throws IOException {
    FeatureType schema = getSchema();       
    String typeName = schema.getTypeName();
   
    if( query.getTypeName() == null ){ // typeName unspecified we will "any" use a default
        DefaultQuery defaultQuery = new DefaultQuery(query);
        defaultQuery.setTypeName( typeName );
    }
   
    if( !typeName.equals( query.getTypeName() ) ){
        return new EmptyFeatureCollection( schema );
    }
    else {
        return new DefaultFeatureResults(this, query);   
    }       
}



Thanks for the idea Andrea!

Related Topics >>