13. SODA Evaluations
In the SODA API chapter we already mentioned Evaluations as a means of providing user-defined custom constraints and as a means to run any arbitrary code in a SODA query. Let's have a closer look.
13.1. Evaluation API
The evaluation API consists of two interfaces, Evaluation and Candidate . Evaluation implementations are implemented by the user and injected into a query. During a query, they will be called from db4o with a candidate instance in order to decide whether to include it into the current (sub-)result.
The Evaluation interface contains a single method only:
This will be called by db4o to check whether the object encapsulated by this candidate should be included into the current candidate set.
The Candidate interface provides three methods:
An Evaluation implementation may call #getObject() to retrieve the actual object instance to be evaluated, it may call #include() to instruct db4o whether or not to include this object in the current candidate set, and finally it may access the current database directly by calling #objectContainer() .
For a simple example, let's go back to our Pilot/Car implementation from the Collections chapter. Back then, we kept a history of SensorReadout instances in a List member inside the car. Now imagine that we wanted to retrieve all cars that have assembled an even number of history entries. A quite contrived and seemingly trivial example, however, it gets us into trouble: Collections are transparent to the query API, it just 'looks through' them at their respective members.
So how can we get this done? Let's implement an Evaluation that expects the objects passed in to be instances of type Car and checks their history size.
To test it, let's add two cars with history sizes of one, respectively two:
and run our evaluation against them:
While evaluations offer you another degree of freedom for assembling queries, they come at a certain cost: As you may already have noticed from the example, evaluations work on the fully instantiated objects, while 'normal' queries peek into the database file directly. So there's a certain performance penalty for the object instantiation, which is wasted if the object is not included into the candidate set.
Another restriction is that, while 'normal' queries can bypass encapsulation and access candidates' private members directly, evaluations are bound to use their external API, just as in the language itself.
One last hint: Evaluations are expected to be serializable for client/server operation. So be careful when implementing them as (anonymous) inner classes and keep in mind that those will carry an implicit reference to their surrounding class and everything that belongs to it. Best practice is to always implement evaluations as normal top level or static inner classes.
With the introduction of evaluations we finally completed our query toolbox. Evaluations provide a simple way of assemble arbitrary custom query building blocks, however, they come at a price.
13.5. Full source