Querying the catalog
How to search and list content by title, description, interface, location, etc.
The Catalog Tool has an easy and clean API to search for content. First of all, you need to acquire it from the current context. Here context can be any object in the site:
from Products.CMFCore.utils import getToolByName catalog = getToolByName(context, 'portal_catalog')
To search for something and get the resulting brains, write:
results = catalog.searchResults(**kwargs)
Where kwargs is a dictionary of index names and their associated query values. Only the indexes that you care about need to be included. This is really useful if you have variable searching criteria, for example, coming from a form where the users can select different fields to search for. For example:
results = catalog.searchResults({'portal_type': 'Event', 'review_state': 'pending'})It is worth pointing out at this point that the indexes that you include are treated as a logical AND, rather than OR. In other words, the query above will find all the items that are both an Event, AND in the review state of pending.
Additionally, you can call the catalog tool directly, which is equivalent to calling catalog.searchResults():
results = catalog(portal_type='Event')
Available indexes
To see the full list of available indexes in your catalog, open the ZMI (what usually means navigating to http://yoursiteURL/manage) look for the portal_catalog object tool into the root of your Plone site and check the Indexes tab. Note that there are different types of indexes, and each one admits different types of search parameters, and behave differently. For example, FieldIndex and KeywordIndex support sorting, but TextIndex doesn't. To learn more about indexes, see The Zope Book, Searching and Categorizing Content.
Some of the most commonly used ones are:
- Title
- The title of the content object.
- Description
- The description field of the content.
- Subject
- The keywords used to categorize the content. Example:
catalog.searchResults(Subject=('cats', 'dogs')) - portal_type
- As its name suggest, search for content whose portal type is indicated. For example:
catalog.searchResults(portal_type='News Item')
You can also specify several types using a list or tuple format:
catalog.searchResults(portal_type=('News Item', 'Event')) - review_state
- The current workflow review state of the content. For example:
catalog.searchResults(review_state='pending')
- object_provides
- From Plone 3, you can search by the interface provided by the content. Example:
from Products.MyProduct.path.to import IIsCauseForCelebration catalog(object_provides=IIsCauseForCelebration.__identifier__)
Searching for interfaces can have some benefits. Suppose you have several types, for example, event types like Birthday, Wedding and Graduation, in your portal which implement the same interface (for example,IIsCauseForCelebration). Suppose you want to get items of these types from the catalog by their interface. This is more exact than naming the types explicitly (like portal_type=['Birthday','Wedding','Graduation' ]), because you don't really care what the types' names really are: all you really care for is the interface.
This has the additional advantage that if products added or modified later add types which implement the interface, these new types will also show up in your query.
Sorting and limiting the number of results
To sort the results, use the sort_on and sort_order arguments. The sort_on argument accepts any available index, even if you're not searching by it. The sort_order can be either 'ascending' or 'descending', where 'ascending' means from A to Z for a text field. 'reverse' is an alias equivalent to 'descending'. For example:
results = catalog_searchResults(Description='Plone documentation', sort_on='sortable_title', sort_order='ascending')
The catalog.searchResults() returns a list-like object, so to limit the number of results you can just use Python's slicing. For example, to get only the first 3 items:
results = catalog_searchResults(Description='Plone documentation')[:3]
In addition, ZCatalogs allow a sort_limit argument. The sort_limit is only a hint for the search algorhitms and can potentially return a few more items, so it's preferable to use both sort_limit and slicing simultaneously:
limit = 50
results = catalog_searchResults(Description='Plone documentation',
sort_limit=limit)[:limit]Searching for content within a folder
Use the 'path' argument to specify the physical path to the folder you want to search into.
By default, this will match objects into the specified folder and all existing sub-folders. To change this behaviour, pass a dictionary with the keys 'query' and 'depth' to the 'path' argument, where
- 'query' is the physical path, and
- 'depth' can be either 0, which will return only the brain for the path queried against, or some number greater, which will query all items down to that depth (eg, 1 means searching just inside the specified folder, or 2, which means searching inside the folder, and inside all child folders, etc).
The most common use case is listing the contents of an existing folder, which we'll assume to be the context object in this example:
folder_path = '/'.join(context.getPhysicalPath())
results = catalog(path={'query': folder_path, 'depth': 1})Getting the underlying object, its path, and its URL from a brain
As it was said earlier, searching inside the catalog returns catalog brains, not the object themselves. If you want to get the object associated with a brain, do:
brain.getObject()
To get the path of the object without fetching it:
brain.getPath()
which is equivalent to obj.getPhysicalPath().
And finally, to get the URL of the underlying object, usually to provide a link to it:
brain.getURL()
which is equivalent to obj.absolute_url().
