Skip to content

Commit 3a35f59

Browse files
jensensclaude
andcommitted
Document runtime index registration behavior
Extend "Dynamic index registration" in architecture.md with a "Runtime registration" subsection covering what works immediately, what needs a restart, and what needs a manual reindex when addons register indexes after startup. Add cross-reference from addIndex in zcatalog-compat.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 00591e4 commit 3a35f59

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

docs/sources/explanation/architecture.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,52 @@ plone.pgcatalog's SQL query builder. Here is how it gets populated:
273273
The registry is a module-level singleton. Once populated, it is used by both the
274274
write path (`_extract_idx()`) and the read path (`build_query()`).
275275

276+
### Runtime registration
277+
278+
Addons can register new indexes after startup -- either by calling
279+
`catalog.addIndex("my_field", "FieldIndex")` directly or, more commonly, via
280+
GenericSetup profile import (``catalog.xml``). When `addIndex()` is called:
281+
282+
1. The index object is created and stored in `_catalog.indexes` (a
283+
`PersistentMapping`), persisting it across restarts.
284+
2. The index's `meta_type` is looked up in `META_TYPE_MAP`. If found, the
285+
in-memory `IndexRegistry` singleton is updated immediately via
286+
`registry.register()`.
287+
288+
Similarly, `addColumn()` calls `registry.add_metadata()` inline.
289+
290+
**What works immediately** (same transaction, no restart needed):
291+
292+
- **Queries.** The query builder dispatches on the registry, so queries using
293+
the new index name work right away for standard types (FieldIndex,
294+
KeywordIndex, DateIndex, BooleanIndex, UUIDIndex, GopipIndex).
295+
- **New writes.** `extract_idx()` iterates the registry, so any subsequent
296+
`catalog_object()` or `reindexObject()` extracts and stores the new index
297+
value in the `idx` JSONB.
298+
299+
**What requires a Zope restart:**
300+
301+
- **GIN expression indexes for TEXT types.** `_ensure_text_indexes()` only runs
302+
during the startup subscriber. A new ZCTextIndex added at runtime will not
303+
have a GIN index until the next restart. Queries still work (PG falls back
304+
to a sequential scan), just without GIN acceleration.
305+
- **IPGIndexTranslator utilities for DRI / DRIRI.** `_register_dri_translators()`
306+
and `_register_driri_translators()` only run at startup. A
307+
DateRecurringIndex or DateRangeInRangeIndex added at runtime will fall through
308+
to the generic JSONB containment fallback, which does not handle
309+
range/recurrence semantics.
310+
311+
**What requires a manual reindex:**
312+
313+
- **Existing objects.** Objects already in the catalog do not have the new field
314+
in their `idx` JSONB. A `clearFindAndRebuild()` or `refreshCatalog()` is
315+
needed to backfill. This matches ZCatalog's behavior -- adding a new index
316+
never auto-populates it.
317+
318+
**No DDL needed** for standard indexes. Since all index data lives in the
319+
single `idx` JSONB column, no `ALTER TABLE` is required -- a key benefit of
320+
the JSONB design.
321+
276322
## Base class architecture
277323

278324
`PlonePGCatalogTool` inherits from `UniqueObject + Folder` -- not from ZCatalog.

docs/sources/reference/zcatalog-compat.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ These methods behave identically to their ZCatalog counterparts:
2525
| `getIndexDataForRID(rid)` | Search ZCatalog | `idx` JSONB dict for a record |
2626
| `indexes()` | Search ZCatalog | List of index names |
2727
| `schema()` | Search ZCatalog | List of metadata column names |
28-
| `addIndex(name, type, extra)` | Manage ZCatalogIndex Entries | Register a new index |
28+
| `addIndex(name, type, extra)` | Manage ZCatalogIndex Entries | Register a new index (see {doc}`../explanation/architecture` for runtime behavior) |
2929
| `delIndex(name)` | Manage ZCatalogIndex Entries | Remove an index |
3030
| `addColumn(name)` | Manage ZCatalogIndex Entries | Register a metadata column |
3131
| `delColumn(name)` | Manage ZCatalogIndex Entries | Remove a metadata column |

0 commit comments

Comments
 (0)