Home Company Services Portfolio Contact us nav spacer

Adding indexes to the relation catalog

by Izak Burger posted on Feb 14, 2013 10:26 AM last modified Feb 14, 2013 10:26 AM —

zc.relation provides powerful indexing features, half of which are still over my head.

Consider the following use case. Suppose I have a set of questions, which are represented by a Question content type, and a number of answers, represented by an Answer content type. Further suppose that each answer object has a reference, by means of z3c.relationfield, to the Question it answers, and that each of the users on your plone site gets to answer the question once.

Suppose your context is a question, and you want to quickly find Joe's answer to the question in context, how would you do it?

From what my research told me, this isn't possible without iterating through answers for the question, looking for Joe's, or through some other means of indexing. The solution I came up with, eventually, was to add another index to the relation catalog, so that I could limit the results to a specific user. This is how I did it.

import BTrees
from zope.component import getUtility
from zc.relation.interfaces import ICatalog

def from_creator(i, catalog):
    """ This is an indexer for the Creator of the from-end of a relation,
        allowing us to do a lookup for objects belonging to a specific user.
    """
    return i.from_object.Creator()

catalog.addValueIndex(from_creator, btree=BTrees.family32.OI)

With this extra index in place, it is now possible to find Joe's answer.

from zope.component import getUtility
from zope.app.intid.interfaces import IIntIds
from zc.relation.interfaces import ICatalog

intids = getUtility(IIntIds)
catalog = getUtility(ICatalog)
relations = catalog.findRelations({
    'to_id': intids.getId(question),
    'from_creator': 'Joe' })
answers = [relation.from_object for relation in relations]

Since the catalog is persistent, you need to add this index only once, likely in your setuphandlers.

idxs = [x['name'] for x in catalog.iterValueIndexInfo()]
if not 'from_creator' in idxs:
    catalog.addValueIndex(from_creator, btree=BTrees.family32.OI)