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.

    Imports com.db4o.query
    Namespace com.db4o.f1.chapter6
        Public Class EvenHistoryEvaluation
            Implements Evaluation
            Public Sub Evaluate(ByVal candidate As Candidate) Implements Evaluation.Evaluate
                Dim car As chapter3.Car = DirectCast(candidate.GetObject(), chapter3.Car)
                candidate.Include(car.History.Count Mod 2 = 0)
            End Sub
        End Class
    End Namespace


    To test it, let's add two cars with history sizes of one, respectively two:

    [storeCars]
    Dim pilot1 As chapter3.Pilot = New chapter3.Pilot("Michael Schumacher", 100)
    Dim car1 As chapter3.Car = New chapter3.Car("Ferrari")
    car1.Pilot = pilot1
    car1.Snapshot()
    db.[Set](car1)
    Dim pilot2 As chapter3.Pilot = New chapter3.Pilot("Rubens Barrichello", 99)
    Dim car2 As chapter3.Car = New chapter3.Car("BMW")
    car2.Pilot = pilot2
    car2.Snapshot()
    car2.Snapshot()
    db.[Set](car2)


    and run our evaluation against them:

    [queryWithEvaluation]
    Dim query As Query = db.Query()
    query.Constrain(GetType(chapter3.Car))
    query.Constrain(New EvenHistoryEvaluation())
    Dim result As ObjectSet = 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.




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


Imports System.IO
Imports com.db4o.query
Imports com.db4o
Namespace com.db4o.f1.chapter6
    Public Class EvaluationExample
    Inherits Util
        Public Shared Sub Main(ByVal args As String())
            File.Delete(Util.YapFileName)
            Dim db As Global.com.db4o.ObjectContainer = Db4oFactory.OpenFile(Util.YapFileName)
            Try
                StoreCars(db)
                QueryWithEvaluation(db)
            Finally
                db.Close()
            End Try
        End Sub
        Public Shared Sub StoreCars(ByVal db As ObjectContainer)
            Dim pilot1 As chapter3.Pilot = New chapter3.Pilot("Michael Schumacher", 100)
            Dim car1 As chapter3.Car = New chapter3.Car("Ferrari")
            car1.Pilot = pilot1
            car1.Snapshot()
            db.[Set](car1)
            Dim pilot2 As chapter3.Pilot = New chapter3.Pilot("Rubens Barrichello", 99)
            Dim car2 As chapter3.Car = New chapter3.Car("BMW")
            car2.Pilot = pilot2
            car2.Snapshot()
            car2.Snapshot()
            db.[Set](car2)
        End Sub
        Public Shared Sub QueryWithEvaluation(ByVal db As ObjectContainer)
            Dim query As Query = db.Query()
            query.Constrain(GetType(chapter3.Car))
            query.Constrain(New EvenHistoryEvaluation())
            Dim result As ObjectSet = query.Execute()
            Util.ListResult(result)
        End Sub
    End Class
End Namespace




--
generated by
Doctor courtesy of db4objects Inc.