Friday, September 08, 2006

Getting a Groovy S.O.D.A

For all who don't know S.O.D.A., it is a query language for object databases. SODA is used by the successful db4o project - a object oriented database for Java and .NET

Imagine an object as graph. The object itself is a node, every field is a connection to another node, another object. You build up a query by defining an entry point into that graph and setting constraints on the connections to other nodes. I borrow an class from their tutorial, I hope that will be no problem

class Pilot {
String name
int points
}
The above defines a class named Pilot with two properties, in Groovy this means a java like class with getters and setters for name and point. Now look at a query:
Query query=db.query();
query.constrain(Pilot.class);
query.descend("name").constrain("Michael Schumacher");
With this I build a query pattern using Pilot as entry point, looking at the connection "name" and constraining that to Objects equals to "Michael Schumacher". So giving this query a database will write out all Object of class Pilot, with the value "Michael Schumacher" stored in "name"

Pretty neat, isn't it? It is typesafe and such... but a bit long... Well Groovy supports operators, why not try something else:
Query query=db.query();
query.constrain(Pilot.class);
query.name == "Michael Schumacher"
we save the descend and the constraint method. But it gets much better when it comes to connecting query parts for logical operations:
Query query=db.query();
query.constrain(Pilot.class);
Constraint constr=query.descend("name")
.constrain("Michael Schumacher");
query.descend("points")
.constrain(new Integer(99)).and(constr);
This query asks for all Michael Schumacher with 99 points. I think we can do this much better in Groovy:
Query query=db.query();
query.constrain(Pilot.class);
query.name == "Michael Schumacher" && query.points == 99
hail operators! This is clear and simple, we don't need the temporary variable any longer and saved some lines of code. And of course operations as greater, smaller, equal, not, and, or are all possible.

Is it possible to improve that even more?
Query query=db.query();
query.constrain(Pilot.class);
with (query) {
name == "Michael Schumacher" && points == 99
}
Maybe a matter of taste if that version is better, but imagine complex queries. I gues you will be lucky not always having to write "query" everywhere.

Another important part of SODA are evaluations. When we use evaluations, then every object our query had as result, is tested against an evaluation instance we provide, and tells the databse to include it in the result or not.
class NameLengthEvaluation implements Evaluation {
public void evaluate(Candidate candidate) {
Pilot p = (Pilot) candidate.getObject()
candidate.include( p.getName().length() < 5 )
}
}
query="db.query();"
This would get us all Pilots with a name of a length of less than 5. A small helper class will give us very much power for groovy:
class ClosureEvaluation implements Evaluation {
ClosureEvaluation(c) {this.closure = c}
def closure
public void evaluate(Candidate candidate) {
candidate.include(closure(candidate.object))
}
}
using this class we can use a closure as evaluation:
Query query=db.query()
query.constrain(Pilot.class)
query.constrain (new ClosureEvaluation() {
it.name.length()<5
})
overloading the constrain method we can even shorten this:
Query query=db.query()
query.constrain(Pilot.class)
query.constrain { it.name.length() < 5 }
Now it is very short, isn't it? We can also use a variant of the closure with two parameters to allow access to the object container, but given the fact, that we use a closure we can simply reuse already declared variables outside the closure.

Conclusion:
Yes, db4o provides a solution in Java too. I mean a shorter form of SODA queries named native queries. Basically the bytecode of the class is loaded, analyzed and transformed in a set of SODA queries and evaluations. Given these short forms in Groovy I am not sure I "need" native Queries.

5 comments:

Carl Rosenberger said...

Blogged.
This is by far the nicest work on top of SODA I have seen.

Maik Jablonski said...

Very nice idea, maybe I'm going to re-evaluate Groovy as "ad-hoc"-query-interpreter for db4o instead of BeanShell.

Jochen "blackdrag" Theodorou said...

maik, I think I will soon commit a SODA query package, that should work with db4o and any other SODA compatible query System.

Dmitriy said...

Very nice idea,
I see last post is dated about 2 years ago. Is there any changes regarding sharing the code?
thanks

Jochen "blackdrag" Theodorou said...

Dmitriy, I have no done anything in that direction, but in fact it wouldn't be very difficult to do so