[Zope] Searching multiple catalogs

Duncan Booth duncan.booth at suttoncourtenay.org.uk
Fri Mar 18 06:26:00 EST 2005


I wrote some code to search multiple catalogs and merge the results, and 
I've got it working, but I don't really understand why LazyCat works the 
way it does, so I'm concerned in case there are situations where what I've 
done won't work properly.

Normally when you search a catalog you get back a LazyMap which applies a 
function to the appropriate result when you call __getitem__ so that it 
appears to contain a list of brains but actually only creates them as 
needed.

If you call searchResults with _merge=0 then you can call mergeResults with 
multiple result sets and get back a LazyCat. Calling __getitem__ on the 
LazyCat returns a 3-tuple and you can construct a brains object from the 
tuple. What I don't understand is why LazyCat doesn't do that final step of 
constructing the brains itself? In particular, does it mean that there is 
some situation that I haven't hit yet where something else gets returned 
and the brains cannot be constructed this way?

Since I want to use the same template to display search results whether 
they come from one or several catalogs, I wrapped my result in a class to 
convert the LazyCat elements into brains, and as I said above this seems to 
work but I'm worried that since I don't really understand the thinking 
behind this code there may be edge cases which break. Can anyone help 
clarify this?

The code I've ended up with:

class LazyCatToBrains:
    '''Lazycat returns tuples instead of converting to brains
    so we need to convert the tuples into brains ourselves when a LazyCat
    item is accessed.
    '''
    __allow_access_to_unprotected_subobjects__=1
    def __init__(self, lc):
        self.seq = lc
        
    def __getitem__(self, index):
        v = self.seq[index]
        if isinstance(v, tuple) and len(v)==3:
            v = v[2](v[1])
        return v

    def __getattr__(self, name):
        return getattr(self.seq, name)

... the further down in the main class ...

    # Based on code by Casey Duncan
    security.declarePublic('queryMultipleCatalogs')
    def queryMultipleCatalogs(self, request, *zcatalogs):
        results = []
        for zcat in zcatalogs:
            results.append(zcat._catalog.searchResults(request, _merge=0))

        sorted = request.has_key('sort-on') or request.has_key('sort_on')
        reverse = ((request.get('sort-order','') or 
                   request.get('sort_order','')).lower() 
                   in ('reverse','descending'))
        results= mergeResults(results, sorted, reverse)
        return LazyCatToBrains(results)

and then the script which actually issues the query can just issue a query 
directly to one catalogue or can query multiple catalogs and handle the 
results identically:

    if REQUEST.get('Occurrences')=='all':
        catalogs = context.multisite_catalog, catalog
        results = context.portal_indexer.queryMultipleCatalogs(query, 
*catalogs)
    else: #'this'
        results=catalog(query)



More information about the Zope mailing list