Rebuild or reindex the catalog

When to rebuild

  • After enabling BM25 (new columns need populating)

  • After upgrading plone.pgcatalog (if release notes mention schema changes)

  • After manual database restoration

  • When catalog counts do not match actual content

Full rebuild (clearFindAndRebuild)

Clears all catalog data and re-indexes every object by traversing the site.

Via ZMI:

Navigate to portal_catalog > Advanced tab 2. Click “Clear and Rebuild”

Via script:

catalog = portal.portal_catalog
catalog.clearFindAndRebuild()
import transaction; transaction.commit()

Expected timing: approximately 15 ms per object.

Selective reindex (reindexIndex)

Re-extracts a single index for all objects:

catalog.reindexIndex("review_state")
import transaction; transaction.commit()

Useful after changing an indexer or adding a new index.

Partial reindex (automatic)

When Plone calls reindexObject(idxs=["review_state"]), plone.pgcatalog uses a lightweight JSONB merge (|| operator) instead of full re-extraction. This happens automatically and does not trigger ZODB serialization of the object.

Choosing the right operation

Operation

Clears data?

Traverses site?

Speed

Use when

clearFindAndRebuild()

Yes

Yes

~15 ms/obj

Schema changes, corrupt data, major upgrades

refreshCatalog(clear=0)

No

Re-catalogs existing

~15 ms/obj

Reindex all without losing uncataloged objects

refreshCatalog(clear=1)

Yes

Yes

Same

Equivalent to clearFindAndRebuild()

reindexIndex("name")

No (single key)

No (PG only)

Fast

Single index changed, new indexer deployed

clearFindAndRebuild() NULLs all catalog columns (path, idx, searchable_text, backend extras), then traverses the entire portal tree and calls catalog_object() on every object found. Use this when catalog data might be inconsistent with actual content.

refreshCatalog(clear=0) reads all cataloged paths from PostgreSQL, resolves each from ZODB, and re-extracts index values. It does not discover objects that were never cataloged.

reindexIndex("name") is a PostgreSQL-only operation: it reads the existing idx JSONB for all objects that have the named key and re-applies it. It does not re-extract values from the live Zope object. To re-extract from objects, use refreshCatalog().

Troubleshooting

  • Verify indexed object count in the ZMI Catalog tab.

  • Check PostgreSQL directly:

    SELECT COUNT(*) FROM object_state WHERE path IS NOT NULL AND idx IS NOT NULL;
    
  • If counts do not match: run clearFindAndRebuild().

  • If individual objects are missing: re-save the object in Plone (triggers reindexObject()).