SODA Query API

The SODA query API is db4o's low level querying API, allowing direct access to nodes of query graphs. Since SODA uses strings to identify fields, it is neither perfectly typesafe nor compile-time checked and it also is quite verbose to write.

For most applications Native Queries will be the better querying interface.

However there can be applications where dynamic generation of queries is required, that's why SODA is explained here.

Simple Queries

Let's see how our familiar QBE queries are expressed with SODA. A new Query object is created through the #query() method of the ObjectContainer and we can add Constraint instances to it. To find all Pilot instances, we constrain the query with the Pilot class object.

QueryExample.cs: RetrieveAllPilots
01public static void RetrieveAllPilots() 02 { 03 IObjectContainer db = Db4oFactory.OpenFile(YapFileName); 04 try 05 { 06 IQuery query = db.Query(); 07 query.Constrain(typeof(Pilot)); 08 IObjectSet result = query.Execute(); 09 ListResult(result); 10 } 11 finally 12 { 13 db.Close(); 14 } 15 }

QueryExample.vb: RetrieveAllPilots
01Public Shared Sub RetrieveAllPilots() 02 Dim db As IObjectContainer = Db4oFactory.OpenFile(YapFileName) 03 Try 04 Dim query As IQuery = db.Query() 05 query.Constrain(GetType(Pilot)) 06 Dim result As IObjectSet = query.Execute() 07 ListResult(result) 08 Finally 09 db.Close() 10 End Try 11 End Sub

Basically, we are exchanging our 'real' prototype for a meta description of the objects we'd like to hunt down: a query graph made up of query nodes and constraints. A query node is a placeholder for a candidate object, a constraint decides whether to add or exclude candidates from the result.

Our first simple graph looks like this.

We're just asking any candidate object (here: any object in the database) to be of type Pilot to aggregate our result.

To retrieve a pilot by name, we have to further constrain the candidate pilots by descending to their name field and constraining this with the respective candidate String.

QueryExample.cs: RetrievePilotByName
1public static void RetrievePilotByName(IObjectContainer db) 2 { 3 IQuery query = db.Query(); 4 query.Constrain(typeof(Pilot)); 5 query.Descend("_name").Constrain("Michael Schumacher"); 6 IObjectSet result = query.Execute(); 7 ListResult(result); 8 }

QueryExample.vb: RetrievePilotByName
1Public Shared Sub RetrievePilotByName(ByVal db As IObjectContainer) 2 Dim query As IQuery = db.Query() 3 query.Constrain(GetType(Pilot)) 4 query.Descend("_name").Constrain("Michael Schumacher") 5 Dim result As IObjectSet = query.Execute() 6 ListResult(result) 7 End Sub

What does 'descend' mean here? Well, just as we did in our 'real' prototypes, we can attach constraints to child members of our candidates.

So a candidate needs to be of type Pilot and have a member named 'name'that is equal to the given String to be accepted for the result.

Note that the class constraint is not required: If we left it out, we would query for all objects that contain a 'name' member with the given value. In most cases this will not be the desired behavior, though.

Finding a pilot by exact points is analogous.We just have to cross the Java primitive/object divide.

QueryExample.cs: RetrievePilotByExactPoints
1public static void RetrievePilotByExactPoints(IObjectContainer db) 2 { 3 IQuery query = db.Query(); 4 query.Constrain(typeof(Pilot)); 5 query.Descend("_points").Constrain(100); 6 IObjectSet result = query.Execute(); 7 ListResult(result); 8 }

QueryExample.vb: RetrievePilotByExactPoints
1Public Shared Sub RetrievePilotByExactPoints(ByVal db As IObjectContainer) 2 Dim query As IQuery = db.Query() 3 query.Constrain(GetType(Pilot)) 4 query.Descend("_points").Constrain(100) 5 Dim result As IObjectSet = query.Execute() 6 ListResult(result) 7 End Sub

Advanced Queries

There are occasions when we don't want to query for exact field values, but rather for value ranges, objects not containing given member values, etc. This functionality is provided by the Constraint API.

First, let's negate a query to find all pilots who are not Michael Schumacher:

QueryExample.cs: RetrieveByNegation
1public static void RetrieveByNegation(IObjectContainer db) 2 { 3 IQuery query = db.Query(); 4 query.Constrain(typeof(Pilot)); 5 query.Descend("_name").Constrain("Michael Schumacher").Not(); 6 IObjectSet result = query.Execute(); 7 ListResult(result); 8 }

QueryExample.vb: RetrieveByNegation
1Public Shared Sub RetrieveByNegation(ByVal db As IObjectContainer) 2 Dim query As IQuery = db.Query() 3 query.Constrain(GetType(Pilot)) 4 query.Descend("_name").Constrain("Michael Schumacher").[Not]() 5 Dim result As IObjectSet = query.Execute() 6 ListResult(result) 7 End Sub

Where there is negation, the other boolean operators can't be too far.

QueryExample.cs: RetrieveByConjunction
01public static void RetrieveByConjunction(IObjectContainer db) 02 { 03 IQuery query = db.Query(); 04 query.Constrain(typeof(Pilot)); 05 IConstraint constr = query.Descend("_name") 06 .Constrain("Michael Schumacher"); 07 query.Descend("_points") 08 .Constrain(99).And(constr); 09 IObjectSet result = query.Execute(); 10 ListResult(result); 11 }

QueryExample.vb: RetrieveByConjunction
1Public Shared Sub RetrieveByConjunction(ByVal db As IObjectContainer) 2 Dim query As IQuery = db.Query() 3 query.Constrain(GetType(Pilot)) 4 Dim constr As Constraint = query.Descend("_name").Constrain("Michael Schumacher") 5 query.Descend("_points").Constrain(99).[And](constr) 6 Dim result As IObjectSet = query.Execute() 7 ListResult(result) 8 End Sub

QueryExample.cs: RetrieveByDisjunction
01public static void RetrieveByDisjunction(IObjectContainer db) 02 { 03 IQuery query = db.Query(); 04 query.Constrain(typeof(Pilot)); 05 IConstraint constr = query.Descend("_name") 06 .Constrain("Michael Schumacher"); 07 query.Descend("_points") 08 .Constrain(99).Or(constr); 09 IObjectSet result = query.Execute(); 10 ListResult(result); 11 }

QueryExample.vb: RetrieveByDisjunction
1Public Shared Sub RetrieveByDisjunction(ByVal db As IObjectContainer) 2 Dim query As IQuery = db.Query() 3 query.Constrain(GetType(Pilot)) 4 Dim constr As Constraint = query.Descend("_name").Constrain("Michael Schumacher") 5 query.Descend("_points").Constrain(99).[Or](constr) 6 Dim result As IObjectSet = query.Execute() 7 ListResult(result) 8 End Sub

We can also constrain to a comparison with a given value.

QueryExample.cs: RetrieveByComparison
1public static void RetrieveByComparison(IObjectContainer db) 2 { 3 IQuery query = db.Query(); 4 query.Constrain(typeof(Pilot)); 5 query.Descend("_points") 6 .Constrain(99).Greater(); 7 IObjectSet result = query.Execute(); 8 ListResult(result); 9 }

QueryExample.vb: RetrieveByComparison
1Public Shared Sub RetrieveByComparison(ByVal db As IObjectContainer) 2 Dim query As IQuery = db.Query() 3 query.Constrain(GetType(Pilot)) 4 query.Descend("_points").Constrain(99).Greater() 5 Dim result As IObjectSet = query.Execute() 6 ListResult(result) 7 End Sub

The query API also allows to query for field default values.

QueryExample.cs: RetrieveByDefaultFieldValue
01public static void RetrieveByDefaultFieldValue(IObjectContainer db) 02 { 03 Pilot somebody = new Pilot("Somebody else", 0); 04 db.Set(somebody); 05 IQuery query = db.Query(); 06 query.Constrain(typeof(Pilot)); 07 query.Descend("_points").Constrain(0); 08 IObjectSet result = query.Execute(); 09 ListResult(result); 10 db.Delete(somebody); 11 }

QueryExample.vb: RetrieveByDefaultFieldValue
01Public Shared Sub RetrieveByDefaultFieldValue(ByVal db As IObjectContainer) 02 Dim somebody As Pilot = New Pilot("Somebody else", 0) 03 db.[Set](somebody) 04 Dim query As IQuery = db.Query() 05 query.Constrain(GetType(Pilot)) 06 query.Descend("_points").Constrain(0) 07 Dim result As IObjectSet = query.Execute() 08 ListResult(result) 09 db.Delete(somebody) 10 End Sub

It is also possible to have db4o sort the results.

QueryExample.cs: RetrieveSorted
01public static void RetrieveSorted(IObjectContainer db) 02 { 03 IQuery query = db.Query(); 04 query.Constrain(typeof(Pilot)); 05 query.Descend("_name").OrderAscending(); 06 IObjectSet result = query.Execute(); 07 ListResult(result); 08 query.Descend("_name").OrderDescending(); 09 result = query.Execute(); 10 ListResult(result); 11 }

QueryExample.vb: RetrieveSorted
01Public Shared Sub RetrieveSorted(ByVal db As IObjectContainer) 02 Dim query As IQuery = db.Query() 03 query.Constrain(GetType(Pilot)) 04 query.Descend("_name").OrderAscending() 05 Dim result As IObjectSet = query.Execute() 06 ListResult(result) 07 query.Descend("_name").OrderDescending() 08 result = query.Execute() 09 ListResult(result) 10 End Sub

All these techniques can be combined arbitrarily, of course. Please try it out. There still may be cases left where the predefined query API constraints may not be sufficient - don't worry, you can always let db4o run any arbitrary code that you provide in an Evaluation. Evaluations will be discussed in a Evaluations chapter.