Monday, April 03, 2006

AdvancedQuery: discovering a new wrinkle

AdvancedQuery by Dieter Mauer is one of my favorite zope extensions. It gives the ZCatalog, zope's indexing system for the zodb the ability to do SQL-like and even more advanced queries against a set of indexes.

While doing some work on wicked, I discovered another fantastic piece of syntactic sugar. The query objects that you use to build the queries support inplace operations. This allows for the elegant creation of aggregate queries. consider this contrived example for a keyword index::

>>> from Products.AdvancedQuery import Eq
>>> tags = ['dogs', 'love', 'trucks']
>>> def findADate(gender, interests, iam_easy=True):
... q = lambda tag: Eq('keywords', tag) & Eq('gender', 'gender')
... # gotta start with something
... query=q(interest[0])
... for interest in interest[1:]:
... if iam_easy: query |= q(interest)
... else: query &= q(interest)
... return query

once we've built a query, we can use it
with the special method AdvQ adds to all catalogs

>>> from Products.CMFCore.utils import getToolByName
>>> catalog = getToolByName(app.portal, 'portal_catalog')
>>> q = findADate('female', tags)
>>> listofdates = catalog.evalAdvancedQuery(q)

Representing queries as objects is a bit more cumbersome for getting started than existing query languages, but being able to programatically build queries with the overhead of awkward string parsing is a big win. The inplace "or" is icing on the cake.

I could see AdvancedQuery forming the basis for a sqlish query language for the catalog and ZODB, something many rdb lovers complain is missing from the world of zope. Since all statements would be backed by objects, programmatically creating queries would be as easy as just demonstrated.

note: |= and &= are also available for sets in python