Addon integrations

plone.pgcatalog replaces ZCatalog’s BTree storage with PostgreSQL queries. This page explains how third-party Plone add-ons interact with the PostgreSQL-backed catalog and why compatibility works the way it does.

How addons integrate automatically

Most Plone addons register their indexes via GenericSetup catalog.xml. These indexes are stored in _catalog.indexes (a PersistentMapping) and are discovered by plone.pgcatalog at startup through sync_from_catalog(), which reads each Plone site’s portal_catalog._catalog.indexes and populates the IndexRegistry.

Because the discovery is dynamic, addons that follow the standard catalog.xml pattern require no special configuration. Their index values are extracted during catalog_object() and stored in the idx JSONB column alongside all other indexes.

Addons that bypass the public catalog API and call ZCatalog internals directly (for example, _apply_index()) need compatibility adapters. These live in the addons_compat/ package.

eea.facetednavigation

PGFacetedCatalog in addons_compat/eeafacetednavigation.py overrides eea.facetednavigation.search.catalog.FacetedCatalog.apply_index() to query PostgreSQL directly via the idx JSONB column instead of ZCatalog’s BTree-based _apply_index().

It dispatches by IndexType from the IndexRegistry:

IndexType

SQL

FIELD, GOPIP, UUID

idx @> '{"key:" "value"}'::jsonb

KEYWORD (single)

idx @> '{"key:" ["value"]}'::jsonb

KEYWORD (multi)

`idx->’{key}’ ?

BOOLEAN

idx @> '{"key:" true}'::jsonb

DATE

idx @> '{"key:" "iso-date"}'::jsonb

Special/unknown

frozenset()

Falls back to IPGIndexTranslator utilities, then to the original BTree-based implementation when the catalog is not IPGCatalogTool.

Activation

The adapter is registered via overrides.zcml (loaded by five:loadProductsOverrides), conditionally included only when eea.facetednavigation is installed:

<includeOverrides
    zcml:condition="installed eea.facetednavigation"
    package=".addons_compat"
    file="eeafacetednavigation_overrides.zcml"
    />

Limitations

Special indexes (SearchableText, effectiveRange, path) are not handled by apply_index() because they use dedicated PostgreSQL columns and query patterns. For these, the adapter returns frozenset() – eea.facetednavigation then falls back to its own handling.

Error recovery

If a PostgreSQL query fails, the adapter catches the exception, logs it via log.exception(), and falls back to the original BTree-based FacetedCatalog.apply_index(). This ensures the site remains functional even if the PG connection is temporarily unavailable.

plone.app.multilingual

plone.app.multilingual adds Language and TranslationGroup indexes to the catalog. These are not standard ZCatalog index types, so they are not present in META_TYPE_MAP.

plone.pgcatalog handles them via the unregistered-index fallback path in query.py: the query builder first checks for an IPGIndexTranslator named utility, then falls back to a JSONB containment query:

-- catalog(Language="en") becomes:
SELECT zoid FROM object_state WHERE idx @> '{"Language": "en"}'::jsonb

No special configuration is needed. The index values are extracted during the standard _extract_idx() path because plone.app.multilingual registers a plone.indexer adapter.

collective.taxonomy

collective.taxonomy registers custom FieldIndex instances via catalog.xml (for example, taxonomy_topic, taxonomy_region). These are autodiscovered at startup by sync_from_catalog() and stored as standard FIELD type entries in the IndexRegistry.

Queries work out of the box:

catalog(taxonomy_topic="science")

Products.DateRangeInRangeIndex

The DateRangeInRangeIndex (from collective.DateRangeInRangeIndex or plone.app.event) tests whether an object’s [start, end] date range overlaps a query range.

plone.pgcatalog provides native support via the DateRangeInRangeIndexTranslator (IPGIndexTranslator utility), autodiscovered at startup. The translator reads the startindex and endindex configuration from each DateRangeInRangeIndex object and generates an overlap SQL query.

Writing your own integration

If your addon needs custom query translation or index extraction beyond what the default JSONB containment provides, implement an IPGIndexTranslator named utility.

See Write a custom index translator for a step-by-step guide.