10.3.4. Contextualisation of queries
A query consists of a series of steps. Some of these steps must be contextualised when a query moves into a context as an aspect (e.g. in the form of a Calculated role, or as the condition of a state):
-
the role step: moving from a context instance to a role instance of a particular type R;
-
the filled role step: moving from a role instance to another instance of a particular type R, that is filled by it.
Referring back to our example above: assume a query defined for case Body, that moves to instances of role Accounts. Clearly, when we start with an instance of CouchdbServer (having Body as aspect), we will not find role instances of Body$Accounts on it; instead, we should contextualise the query step from Account to CouchdbServer$Accounts
.
It turns out that runtime contextualization is rather easy for the role step (for a more detailed treatment we refer back to Contextualizing Queries). For a given context type, we can compile an alias administration that maps, for each Enumerated role type available in the context, its supertypes onto that Enumerated role. Then, when a role step has to be carried out on a context instance by the query evaluator, we have it
-
look up the type of the context instance;
-
fetch the alias administration from it;
-
look up the role step type to find the appropriate Enumerated role type
-
use that to look up instances on the context instance.
This causes some overhead during query evaluation, but saves a great deal of space with inverted queries. Remember that a query is inverted for reasons of synchronization and state transition and that the inversions are stored with each role and context type that is part of the query. When we contextualize a query in compile time, we have to invert the contextualized version, too.
If we rely on runtime contextualization, when a role instance is added to a context instance, we have to look up inverted queries on the type of the role instance and on all its aspect types. Climbing the aspect hierarchy takes a little time, but in the end the same number of queries has to be evaluated.
The filled role step can be contextualized in runtime using a variant of the alias administration. We then need to build this administration in the filler role instances. An example will make this clearer. Suppose we have a pattern with a Driver and a Vehicle role and we use it to specialise a context with a Pilot and a Plane, respectively. The first time we fill a Pilot role instance with some role instance R, we’d have to record on R that Pilot is an alias for Driver. Then, when the filled role step Driver is carried out on R, we look up Driver in the alias administration of R, find Pilot, then read from R what role instances are recorded as filled under the key Pilot.
Overall, we find that run time contextualisation of queries is conceptually simpler, imposes less storage overhead and introduces an acceptable runtime penalty.