Add indexing attributes to arbitrary content types
Plone's portal_catalog is a very powerful mechanism for retrieving portions
of object content under Plone. Not only are searches fast, the
search return contains embedded metadata that in many cases make
it unnecessary to fetch the object itself, which yields a further
speed advantage.
The indexes associated with an object need not be limited to
the content of fields. Indexes can contain values computed by
a method -- even values computed from other content or other
catalogs. This yields tremendous flexibility in design
on top of the speed advantages of catalog retrieval.
This is all very exciting, but as you begin exploring the varied and wonderful possibilities of computed index values, you must beware of the temptation to add index attributes to content types offered by products maintained (or not) by other people. Subclassing or hacking the code of someone else's product is messy, and commits you to a maintenance burden of unknown future cost. It is not something to undertake lightly.
As it happens, though, from Plone 2.1.x forward it is possible to add
arbitrary index attributes to any Plone content type without
touching the code of the product that supplies it,
using an
undocumented facility embedded in the Plone portal catalog.
(A tip of the hat to optilude for flagging
this issue, I hope this small
contribution helps.) The function within the catalog
that does this is called (as you might have expected)
ExtensibleIndexableObjectWrapper, and the method used to
invoke it has the only slightly less Teutonic appellation
registerIndexableAttribute. [1]
Despite the intimidating length of the names involved, extending
the catalog to apply additional methods to some or all of the objects it contains
is quite simple to do.
From Plone 3.3, the Extensible Indexable Object Wrapper can be used as an adapter. See Indexing and Searching - Custom indexing strategies to learn how.
The code to create global index attributes needs to be spliced in via
the initialize method of a product. The first step
is to choose an appropriate
product (or create one), and in its __init__.py file, add
a function like this (this goes outside of the initialize
function statement):
def _notUseful(obj,portal,vars):
"""A silly method for indexing things in a meaningless way
"""
if obj.getTypeInfo().getId() == 'Folder':
raise AttributeError
else:
return True
What is going on here? First, note that the attribute takes three
arguments: context, portal and vars.
As I'm both lazy and ignorant, I'll going to play it safe and just repeat what the source code says
about these:
- obj
- the indexable object
- portal
- the portal root object
- vars
- additional vars as a dict, used for workflow vars like review_state
Second, note the use of AttributeError in the method above.
Any callables that you register will be invoked on every object
that is placed in the catalog. If you want to skip objects
of a particular type (or objects that have a particular interface,
gender, national origin, hair color or sexual preference), raising
AttributeError on those objects will cause
them to be skipped over. The code in the sample method above is,
of course, arbitrary; whatever value the method returns will be
the value placed in the index.
Defining the method may give us a warm feeling, but it is not
actually going to be applied to anything until it is registered
in the catalog. This is done through the initialize method
of the same __init__.py file, like this:
from Products.CMFPlone.CatalogTool import registerIndexableAttribute
def initialize(context):
registerIndexableAttribute('definitelyNotHelpful',_notUseful)
Note that the function name in this example has been carefully chosen to avoid name clashes with attributes supplied by other products. If an object supplies a method with a name identical to our registered attribute, the catalog will prefer the one provided by the object itself; so if you want to be sure your new attribute gets invoked, be sure it is unique.
If you restart Zope and use the ZMI to create a field index named
definitelyNotHelpful, tick its tick-box and press the Reindex button, every
non-Folder object in the catalog will be indexed with the value True.
Hurray.
And, ah, that's it.
Footnotes:
[1] Trivia buffs will be eager to learn that
this pair, impressive though they may be, lose out in the Longest Function Names in Plone 2.5
rankings to removePloneSetupActionFromPortalMembership (weighing in at a robust 42 characters), and
to the more or less incomprehensible runner-up,
addTypesUseViewActionInListingsProperty (a plump but still ambulatory 39 characters).

Author: