Releases: bluedynamics/plone-pgcatalog
v1.0.0b14
What's Changed
Fixed
- Fix new objects not being indexed in PostgreSQL.
ZODB assigns object IDs (_p_oid) duringConnection.commit(), which runs
afterbefore_commithooks (where the IndexQueue flushes). All new objects
therefore have_p_oid=Noneatcatalog_object()call time, causing the
catalog to silently skip them. The fix stores pending catalog data directly
inobj.__dict__under the_pgcatalog_pendingkey when no OID is available
yet;CatalogStateProcessor.process()pops and uses it duringstore()so
the annotation is never persisted to the database.
Fixes #27.
Full Changelog: v1.0.0b13...v1.0.0b14
v1.0.0b13
What's Changed
Fixed
- Preserve original Python types for metadata columns (e.g.
brain.effective
now returns a ZopeDateTimeobject instead of an ISO string).
Non-JSON-native metadata values (DateTime, datetime, date, etc.) are
encoded via the Rust codec intoidx["@meta"]at write time and restored
on brain attribute access with per-brain caching. JSON-native values
(str, int, float, bool, None) remain in top-levelidxunchanged.
Backward compatible — old data without@metastill works.
Thanks @erral for the testing and report.
Fixes #23.
Full Changelog: v1.0.0b12...v1.0.0b13
v1.0.0b12
What's Changed
- Fix
clearFindAndRebuildproducing wrong paths (missing portal id prefix,
e.g./newsinstead of/Plone/news), indexingportal_catalogitself,
and not re-indexing the portal root object.
Now usesgetPhysicalPath()for authoritative paths,aq_base()for
identity comparison through Acquisition wrappers, and explicitly indexes
the portal root before traversal (matching Plone'sCatalogTool).
Thx @erral for reporting and checking. Fixes #21.
Full Changelog: v1.0.0b11...v1.0.0b12
v1.0.0b11
What's Changed
Fixed
-
Fix example
requirements.txt: use local editable path for
pgcatalog-exampleinstead of bare package name (not on PyPI).
Fixes #18. -
Fix ZMI "Update Catalog" and "Clear and Rebuild" buttons returning 404.
Added missingmanage_catalogReindexandmanage_catalogRebuildmethods.
Fixes #19. -
Fix
clearFindAndRebuildindexing non-content objects (e.g.acl_users).
Now filters for contentish objects only (those with areindexObjectmethod),
matching Plone'sCatalogToolbehavior.
Fixes #20.
Changed
-
uniqueValuesFor(name)is now a supported API (no longer deprecated).
It delegates tocatalog.Indexes[name].uniqueValues(). -
Move
driri.pyto addons_compat/ in @17
Full Changelog: 1.0.0b10...v1.0.0b11
1.0.0b10
What's Changed
Changed
-
Clean break from ZCatalog:
PlonePGCatalogToolno longer inherits
fromProducts.CMFPlone.CatalogTool(and transitivelyZCatalog,
ObjectManager, etc.). The new base classes areUniqueObject + Folder,
providing a minimal OFS container for index objects and lexicons while
eliminating the deep inheritance chain.This improves query performance by ~2x across most scenarios (reduced
Python-side overhead from attribute lookups, security checks, and
Acquisition wrapping) and write performance by ~5% (lighter commit path).A
_CatalogCompatpersistent object provides_catalog.indexesand
_catalog.schemafor backward compatibility with code that accesses
ZCatalog internal data structures. Existing ZODB instances with the old
_catalog(fullCatalogobject) continue to work without migration. -
ZCML override for eea.facetednavigation: Moved from
<includeOverrides>
insideconfigure.zcmlto a properoverrides.zcmlat the package root,
loaded by Zope'sfive:loadProductsOverrides. Fixes ZCML conflict errors
when both eea.facetednavigation and plone.pgcatalog are installed.
Added
-
eea.facetednavigation adapter:
PGFacetedCatalogin
addons_compat/eeafacetednavigation.py-- PG-backedIFacetedCatalog
that queriesidxJSONB directly for faceted counting. Dispatches by
IndexType(FIELD, KEYWORD, BOOLEAN, UUID, DATE) withIPGIndexTranslator
fallback. Falls back to the default BTree-based implementation when the
catalog is notIPGCatalogTool. Conditionally loaded only when
eea.facetednavigationis installed. -
Deprecated proxy methods:
search()proxies tosearchResults()and
uniqueValuesFor()proxies toIndexes[name].uniqueValues(), both
emittingDeprecationWarning. -
Blocked methods:
getAllBrains,searchAll,getobject,
getMetadataForUID,getMetadataForRID,getIndexDataForUID,
index_objectsraiseNotImplementedErrorwith descriptive messages. -
AccessControl security declarations: Comprehensive Zope security
matching ZCatalog's permission model.Search ZCatalogon read
methods (searchResults,__call__,getpath,getrid, etc.),
Manage ZCatalog Entrieson write methods (catalog_object,
uncatalog_object,refreshCatalog, etc.),Manage ZCatalogIndex Entrieson index management (addIndex,delIndex,addColumn,
delColumn,getIndexObjects).setPermissionDefaultassigns
default roles (Anonymousfor search,Managerfor management).
Private helpers (indexObject,reindexObject, etc.) declared
private. -
DateRangeInRangeIndex support: Native
IPGIndexTranslatorfor
Products.DateRangeInRangeIndexoverlap queries. Translates
catalog({'my_idx': {'start': dt1, 'end': dt2}})into a single SQL
overlap clause (obj_start <= q_end AND obj_end >= q_start).
Supports recurring events: when the underlying start index is a
DateRecurringIndex with RRULE, usesrrule."between"()with duration
offset for occurrence-level overlap detection. Auto-discovered at
startup — no configuration needed. Allows dropping the
Products.DateRangeInRangeIndexaddon while keeping the same query API.
Fixed
- Addon index preservation: Installing plone.pgcatalog on a site with
addon-provided catalog indexes (e.g. fromcollective.taxonomy,
plone.app.multilingual, etc.) no longer silently drops those index
definitions. The install step now snapshots all existing index definitions
and metadata columns before replacingportal_catalog, then restores
addon indexes after re-applying core Plone profiles. Removedtoolset.xml
in favour of a setuphandler-controlled replacement for correct timing.
Full Changelog: 1.0.0b9...1.0.0b10
v1.0.0b8
What's Changed
Changed
-
Module split:
config.pyhas been split into four focused modules:
pending.py(thread-local pending store + savepoint support),
pool.py(connection pool discovery + request-scoped connections),
processor.py(CatalogStateProcessor),
startup.py(IDatabaseOpenedWithRootsubscriber + registry sync).
config.pyis now a deprecation stub. -
Shared
ensure_date_param(): Deduplicated date coercion utility from
query.pyanddri.pyintocolumns.ensure_date_param(). -
__all__exports: Added explicit__all__topending.py,pool.py,
processor.py,startup.py,columns.py,backends.py,interfaces.py. -
Top-level imports: Removed unnecessary deferred imports across
catalog.py,processor.py,startup.py.
Added
-
verifyClass/verifyObjecttests forIPGIndexTranslatorimplementations. -
Shared
query_zoids()test helper inconftest.py.
Security
Security review fixes (addresses #11):
- CAT-C1: Replace f-string DDL in
BM25Backend.install_schema()with
psycopg.sql.SQL/Identifier/Literalcomposition. Validate language
codes againstLANG_TOKENIZER_MAPallowlist +validate_identifier()on
all generated column/index/tokenizer names. - CAT-H1: Clamp
sort_limit/b_sizeto_MAX_LIMIT(10,000) and
b_startto_MAX_OFFSET(1,000,000) to prevent resource exhaustion. - CAT-H2: Validate RRULE strings in
DateRecurringIndexTranslator.extract()
against RFC 5545 pattern and_MAX_RRULE_LENGTH(1,000) before storing. - CAT-H3: Truncate full-text search queries to
_MAX_SEARCH_LENGTH(1,000)
to prevent excessive tsvector parsing. - CAT-M1: Replace f-string SQL in
clear_catalog_data()with
psycopg.sql.Identifierfor extra column names. - CAT-M2: Add
conn.closedguard inrelease_request_connection()to
handle already-closed connections; document pool leak recovery in docstring. - CAT-M3: Add defensive
validate_identifier(index_name)in
DateRecurringIndexTranslator.query(). - CAT-L1: Simplify error messages to not expose internal limit values.
- CAT-L2: Add rate limiting guidance note in
searchResults()docstring. - CAT-L3: Normalize double slashes in
_validate_path().
Full Changelog: v1.0.0b7...v1.0.0b8
1.0.0b9
Changed
-
ZMI polish: All ZMI tabs now use Bootstrap 4 cards/tables matching
Zope 5's modern look (was old-style<table>layout withsection-bar). -
Catalog tab (
manage_catalogView): Replaced inherited ZCatalog
BTree-based view with PG-backed version. Shows catalog summary (object
count, index/metadata count, search backend with BM25/Tsvector status),
path filter, and server-side paginated object table (20/page) with
Previous/Next navigation. Object detail shows full idx JSONB and
searchable text preview. -
Advanced tab (
manage_catalogAdvanced): Simplified to only show
Update Catalog and Clear and Rebuild actions. Removed ZCatalog-specific
features (subtransactions, progress logging, standalone Clear Catalog)
that don't apply to PostgreSQL. -
Indexes & Metadata tab (
manage_catalogIndexesAndMetadata): Merged
the separate Indexes and Metadata tabs into one read-only view showing
all registered indexes (name, type, PG storage location, source attrs)
and metadata columns. Reflects the IndexRegistry rather than BTree
counts (which were always 0). -
Removed tabs: Query Report, Query Plan (BTree timing), and the
separate Indexes / Metadata tabs are hidden — replaced by PG-aware
equivalents. -
Lexicon cleanup:
setuphandlers.install()now removes orphaned
ZCTextIndex lexicons (htmltext_lexicon,plaintext_lexicon,
plone_lexicon) created by Plone'scatalog.xml— unused with
PG-backed text search.
Full Changelog: v1.0.0b8...1.0.0b9
v1.0.0b7
What's Changed
-
sort_onnow accepts a list of index names for multi-column sorting,
matching ZCatalog's API.sort_ordercan also be a list (one direction
per sort key) or a single string applied to all keys. -
PGCatalogBrain.__getattr__now distinguishes known catalog fields from
unknown attributes. Known indexes and metadata columns returnNonewhen
absent from idx (matching ZCatalog's Missing Value behavior), while unknown
attributes raiseAttributeError. This enables
CatalogContentListingObject.__getattr__to fall back togetObject()
for non-catalog attributes (e.g.content_type), and fixes PAM's
get_alternate_languages()viewlet crash onbrain.Language. -
reindexIndexnow acceptspghandlerkeyword argument for compatibility
with ZCatalog'smanage_reindexIndexand plone.distribution. The argument
is accepted but ignored (PG-based reindexing doesn't need progress
reporting). [#9] -
clearFindAndRebuildnow properly rebuilds the catalog by traversing all
content objects after clearing PG data. Previously only cleared without
rebuilding. -
refreshCatalognow properly re-catalogs objects by resolving them from
ZODB and re-extracting index values. Added missingpghandlerparameter
for ZCatalog API compatibility. -
Fixed
ConnectionStateErroron Zope restart when a Plone site already
exists in the database._sync_registry_from_dband
_detect_languages_from_dbnow abort the transaction before closing
their temporary ZODB connections. -
_ensure_catalog_indexesnow checks for essential Plone indexes (UID,
portal_type) instead of any indexes, preventing addon indexes from
blocking re-application of Plone defaults. -
ZCatalog internal API compatibility:
getpath(rid),getrid(path),
Indexes["UID"]._index.get(uuid), anduniqueValues(withLengths=True)
now work with PG-backed data. Uses ZOID as the record ID. This fixes
plone.api.content.get(UID=...),plone.app.vocabulariescontent
validation, and dexterity type counting in the control panel.
Full Changelog: v1.0.0b6...v1.0.0b7
v1.0.0b6
What's Changed
Added
-
Relevance-ranked search results: SearchableText queries now automatically
return results ordered by relevance when no explicitsort_onis specified.
Title matches rank highest (weight A), followed by Description (weight B),
then body text (weight D). Uses PostgreSQL's built-ints_rank_cd()with
cover density ranking. No extensions required.
Note: Requires a full catalog reindex after upgrade. -
Optional BM25 ranking via VectorChord-BM25 extension. When
vchord_bm25
andpg_tokenizerextensions are detected at startup, search results are
automatically ranked using BM25 (IDF, term saturation, length normalization)
instead ofts_rank_cd. Title matches are boosted via combined text.
Vanilla PostgreSQL installations continue using weighted tsvector
ranking with no changes needed.
Requires:vchord_bm25+pg_tokenizerPostgreSQL extensions.
Note: Full catalog reindex required after enabling. -
Per-language BM25 columns: each configured language gets its own
bm25vectorcolumn with a language-specific tokenizer. Supports
30 Snowball stemmers (Arabic to Yiddish), jieba (Chinese), and
lindera (Japanese/Korean). Configure viaPGCATALOG_BM25_LANGUAGES
environment variable (comma-separated codes, orautoto detect from
portal_languages). Fallback column for unconfigured languages ensures
BM25 ranking benefits for all content.
Note: Changing languages requires full catalog reindex. -
SearchBackendabstraction: thin interface for swappable search/ranking
backends.TsvectorBackend(always available) andBM25Backend(optional).
Backend auto-detected at Zope startup. -
LANG_TOKENIZER_MAPinbackends.pymaps ISO 639-1 codes to pg_tokenizer
configurations. Regional variants (pt-br, zh-CN) are normalized to base
codes automatically. -
Estonian (
et) added to language-to-regconfig mapping (supported by PG 17). -
Multilingual example:
create_site.pyzconsole script creates a Plone
site withplone.app.multilingual(EN, DE, ZH), installs plone.pgcatalog,
and imports ~800+ Wikipedia geography articles across all three languages
with PAM translation linking.fetch_wikipedia.pyfetches articles from
en/de/zh Wikipedia with cross-language links. Seeexample/README.md.
Fixed
-
reindexObjectSecuritynow works for newly created objects.
unrestrictedSearchResultsextends PG results with objects from the
thread-local pending store (not yet committed to PG) for path queries.
Previously, newly created objects were invisible to the path search in
CMFCatalogAware.reindexObjectSecurity, so their security indexes
(e.g.allowedRolesAndUsers) were never updated during workflow
transitions in the same transaction. -
CatalogSearchResultsnow implementsIFiniteSequence, enabling
IContentListingadaptation in Plone's search view. -
PGCatalogBrainnow providesgetId(property) andpretty_title_or_id()
for compatibility with Plone's Classic UI navigation and search templates.
getIdis a property (not a method) sobrain.getIdreturns a string,
matching standard ZCatalog brain behavior. -
PGCatalogBrain.__getattr__returnsNonefor missing idx keys instead
of raisingAttributeError, matching ZCatalog's Missing Value behavior.
Fixes PAM'sget_alternate_languages()viewlet crash onbrain.Language. -
Unknown catalog indexes (e.g.
Language,TranslationGroupfrom
plone.app.multilingual) now fall back to JSONB field queries instead of
being silently skipped. This enables PAM's translation registration and
lookup queries to work correctly. -
CJK tokenizer TOML format fixed: jieba (Chinese) and lindera
(Japanese/Korean) now use the correct table syntax for pg_tokenizer's
pre_tokenizerconfiguration.
Full Changelog: v1.0.0b5...v1.0.0b6