The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


The code always knows .. how to handle Query

Posted by jive on February 21, 2007 at 10:40 AM PST
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):
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:
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:
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.
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 >> Open Source      
Comments
Comments are listed in date ascending order (oldest first)