The Occasional Occurence

A Cool New Dejavu Feature

November 18, 2006 at 11:17 AM | categories: Python, work, General

So I was working on something for Amor and came up against a limitation in Dejavu. I was working with a Unit setup similar to this:

::

import datetime from dejavu import Unit, UnitProperty

class Event(Unit):
Name = UnitProperty(unicode) Start = UnitProperty(datetime.datetime) End = UnitProperty(datetime.datetime) # a far Contact.ID ContactID = UnitProperty(int) # also a far Contact.ID HostID = UnitProperty(int)
class Contact(Unit):
FirstName = UnitProperty(unicode) LastName = UnitProperty(unicode) BirthDate = UnitProperty(datetime.date)

Event.many_to_one('ContactID', Contact, 'ID')

Conceptually, Events are associated to two Contacts; a general "contact" for the event and a "host". Dejavu allowed rigging up the first association easily (see Event.many_to_one(...) above). So I had Events and Contacts, and a many-to-one association between them based on the Event.ContactID and the Contact.ID.

That allows some nice recalling (querying) possibilities:

::

from myapp import arena box = arena.new_sandbox()

expr = lambda evt, con: con.LastName == 'Smith' # recall (Event, Contact) pairs where Contact.LastName == 'Smith' results = box.recall(Event & Contact, expr)

But what if I wanted to recall all Events and Contacts based on some attribute of the host Contact? I could create a custom UnitAssociation, which Dejavu provides for:

::
class EventHostAssociation(UnitAssociation):

to_many = False register = False

def related(self, unit, expr=None):
'''Define a magic recaller method...''' host = unit.sandbox.unit(Contact, ID=unit.HostID) return host

descriptor = EventHostAssociation('HostID', Contact, 'ID') descriptor.nearClass = Event Event._associations['Event Host'] = Event.Host = descriptor

Once I had recalled some Event units, I could call Event.Host() on them to get the related Contact. That is convenient, but for doing large scale operations based on some attribute of the host Contact, it would mean many potential hits on the underlying storage mechanism (probably a DB).

The problem was that even though Dejavu allows for custom UnitAssociations, there is no way to define what association path to prefer when doing a multirecall (JOIN). Since the Event>--Contact association was already defined using Event.ContactID and not Event.HostID, I was stuck.

I brought this situation to the attention of Dejavu's author, Robert Brewer. After a short while, he came up with a solution and an idea on how to implement it. I set off to work, and the result is that in the latest Dejavu trunk code, you can do this:

::

#create a UnitJoin object from two related Units join = Event & Contact #set a non-default custom UnitJoin path join.path = 'Event Host'

# setup an expression that will yield Events and (host) Contacts that are # older than a certain demographic expr = lambda evt, hst: hst.BirthDate < datetime.date(1978, 8, 17) results = box.recall(join, expr)

By setting the join.path to 'Event Host', I instructed Dejavu to use the custom UnitAssociation that I created above when performing that particular recall of Events and Contacts. Since the underlying storage mechanism I was using was a database that supports JOINs, I was able to recall all the Events and (host) Contacts that met my criteria in one hit on the DB.

So now Dejavu rocks just a little bit more*.

cw

* This implementation of this functionality as I described it is present in the trunk as of now, but might change somewhat before the next official release of Dejavu (1.5).