| # 10. 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. ## 10.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:
`public void evaluate(Candidate candidate);`
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:
`public Object getObject(); public void include(boolean flag); public ObjectContainer objectContainer();`
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(). ## 10.2. Example 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.
`using com.db4o.f1.chapter3; using com.db4o.query; namespace com.db4o.f1.chapter6 { public class EvenHistoryEvaluation : Evaluation { public void evaluate(Candidate candidate) { Car car=(Car)candidate.getObject(); candidate.include(car.History.Count % 2 == 0); } } }`
To test it, let's add two cars with history sizes of one, respectively two:
`[storeCars] Pilot pilot1 = new Pilot("Michael Schumacher", 100); Car car1 = new Car("Ferrari"); car1.Pilot = pilot1; car1.Snapshot(); db.set(car1); Pilot pilot2 = new Pilot("Rubens Barrichello", 99); Car car2 = new Car("BMW"); car2.Pilot = pilot2; car2.Snapshot(); car2.Snapshot(); db.set(car2);`
and run our evaluation against them:
`[queryWithEvaluation] Query query = db.query(); query.constrain(typeof (Car)); query.constrain(new EvenHistoryEvaluation()); ObjectSet result = query.execute(); Util.listResult(result);`
OUTPUT: 1 BMW[Rubens Barrichello/99]/2

## 10.3. Drawbacks

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 for Javaists: 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.

## 10.4. Conclusion

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.

## 10.5. Full source

`using System.IO; using com.db4o.f1.chapter3; using com.db4o.query; namespace com.db4o.f1.chapter6 { public class EvaluationExample : Util { public static void Main(string[] args) { File.Delete(Util.YapFileName); ObjectContainer db = Db4o.openFile(Util.YapFileName); try { storeCars(db); queryWithEvaluation(db); } finally { db.close(); } } public static void storeCars(ObjectContainer db) { Pilot pilot1 = new Pilot("Michael Schumacher", 100); Car car1 = new Car("Ferrari"); car1.Pilot = pilot1; car1.Snapshot(); db.set(car1); Pilot pilot2 = new Pilot("Rubens Barrichello", 99); Car car2 = new Car("BMW"); car2.Pilot = pilot2; car2.Snapshot(); car2.Snapshot(); db.set(car2); } public static void queryWithEvaluation(ObjectContainer db) { Query query = db.query(); query.constrain(typeof (Car)); query.constrain(new EvenHistoryEvaluation()); ObjectSet result = query.execute(); Util.listResult(result); } } }`

--
generated by Doctor courtesy of db4objects Inc.