From d821b6cb00abaef741860de1f7444c5517629bb2 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Mon, 24 Mar 2025 00:04:39 +0530 Subject: [PATCH 01/14] Add functions to manage catalog indexes and metadata columns --- src/plone/api/portal.py | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/plone/api/portal.py b/src/plone/api/portal.py index a3d21b5c..b14a0530 100644 --- a/src/plone/api/portal.py +++ b/src/plone/api/portal.py @@ -22,6 +22,7 @@ from zope.schema.interfaces import IVocabularyFactory import datetime as dtime +import logging import re @@ -472,3 +473,62 @@ def get_vocabulary_names(): :Example: :ref:`portal-get-all-vocabulary-names-example` """ return sorted([name for name, vocabulary in getUtilitiesFor(IVocabularyFactory)]) + +@required_parameters("wanted_indexes") +def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): + """ + Add the specified indexes to portal_catalog if they don't already exist. + + Parameters: + - wanted_indexes: List of tuples in format (index_name, index_type) + - reindex: Boolean indicating if newly added indexes should be reindexed + - logger: Optional logger instance + + Returns: + - List of newly added index names + """ + if logger is None: + logger = logging.getLogger('plone.api.portal') + + catalog = get_tool('portal_catalog') + existing_indexes = catalog.indexes() + + added_indexes = [] + for name, meta_type in wanted_indexes: + if name not in existing_indexes: + catalog.addIndex(name, meta_type) + added_indexes.append(name) + logger.info('Added %s index for field %s.', meta_type, name) + + if reindex and added_indexes: + logger.info('Reindexing new indexes: %s', ', '.join(added_indexes)) + catalog.manage_reindexIndex(ids=added_indexes) + + return added_indexes + +@required_parameters("wanted_columns") +def add_catalog_metadata(wanted_columns, logger=None): + """ + Add the specified metadata columns to portal_catalog if they don't already exist. + + Parameters: + - wanted_columns: List of column names to add + - logger: Optional logger instance + + Returns: + - List of newly added column names + """ + if logger is None: + logger = logging.getLogger('plone.api.portal') + + catalog = get_tool('portal_catalog') + existing_columns = catalog.schema() + + added_columns = [] + for name in wanted_columns: + if name not in existing_columns: + catalog.addColumn(name) + added_columns.append(name) + logger.info('Added metadata column: %s', name) + + return added_columns \ No newline at end of file From e1d4b24b5c9f723a570ade6f1748d40980208d2b Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Mon, 24 Mar 2025 00:07:14 +0530 Subject: [PATCH 02/14] refactor code --- src/plone/api/portal.py | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/plone/api/portal.py b/src/plone/api/portal.py index b14a0530..4a200065 100644 --- a/src/plone/api/portal.py +++ b/src/plone/api/portal.py @@ -474,61 +474,63 @@ def get_vocabulary_names(): """ return sorted([name for name, vocabulary in getUtilitiesFor(IVocabularyFactory)]) + @required_parameters("wanted_indexes") def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): """ Add the specified indexes to portal_catalog if they don't already exist. - + Parameters: - wanted_indexes: List of tuples in format (index_name, index_type) - reindex: Boolean indicating if newly added indexes should be reindexed - logger: Optional logger instance - + Returns: - List of newly added index names """ if logger is None: - logger = logging.getLogger('plone.api.portal') - - catalog = get_tool('portal_catalog') + logger = logging.getLogger("plone.api.portal") + + catalog = get_tool("portal_catalog") existing_indexes = catalog.indexes() - + added_indexes = [] for name, meta_type in wanted_indexes: if name not in existing_indexes: catalog.addIndex(name, meta_type) added_indexes.append(name) - logger.info('Added %s index for field %s.', meta_type, name) - + logger.info("Added %s index for field %s.", meta_type, name) + if reindex and added_indexes: - logger.info('Reindexing new indexes: %s', ', '.join(added_indexes)) + logger.info("Reindexing new indexes: %s", ", ".join(added_indexes)) catalog.manage_reindexIndex(ids=added_indexes) - + return added_indexes + @required_parameters("wanted_columns") def add_catalog_metadata(wanted_columns, logger=None): """ Add the specified metadata columns to portal_catalog if they don't already exist. - + Parameters: - wanted_columns: List of column names to add - logger: Optional logger instance - + Returns: - List of newly added column names """ if logger is None: - logger = logging.getLogger('plone.api.portal') - - catalog = get_tool('portal_catalog') + logger = logging.getLogger("plone.api.portal") + + catalog = get_tool("portal_catalog") existing_columns = catalog.schema() - + added_columns = [] for name in wanted_columns: if name not in existing_columns: catalog.addColumn(name) added_columns.append(name) - logger.info('Added metadata column: %s', name) - - return added_columns \ No newline at end of file + logger.info("Added metadata column: %s", name) + + return added_columns From 4b338d15184e6e37f81ead543eb82b4461d10f45 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Mon, 24 Mar 2025 00:24:21 +0530 Subject: [PATCH 03/14] Add tests for adding catalog indexes and metadata columns --- src/plone/api/tests/test_portal.py | 98 ++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/plone/api/tests/test_portal.py b/src/plone/api/tests/test_portal.py index a822aab5..d68523a4 100644 --- a/src/plone/api/tests/test_portal.py +++ b/src/plone/api/tests/test_portal.py @@ -963,3 +963,101 @@ def test_vocabulary_terms(self): states = [term.value for term in states_vocabulary] self.assertIn("private", states) self.assertIn("published", states) + + def test_add_catalog_indexes(self): + """Test adding catalog indexes.""" + import logging + + catalog = portal.get_tool('portal_catalog') + initial_indexes = catalog.indexes() + + # Test adding new indexes + test_indexes = [ + ('test_field1', 'FieldIndex'), + ('test_field2', 'KeywordIndex'), + ] + + added = portal.add_catalog_indexes(test_indexes) + + # Verify indexes were added + self.assertEqual(len(added), 2) + self.assertIn('test_field1', added) + self.assertIn('test_field2', added) + self.assertIn('test_field1', catalog.indexes()) + self.assertIn('test_field2', catalog.indexes()) + + # Test adding already existing indexes + added = portal.add_catalog_indexes(test_indexes) + self.assertEqual(len(added), 0) # No new indexes should be added + + # Test with reindex=False + test_indexes2 = [ + ('test_field3', 'FieldIndex'), + ] + + # Create a mock for catalog.manage_reindexIndex to verify it's called or not + original_reindex = catalog.manage_reindexIndex + + try: + reindex_called = [False] + def mock_reindex(ids=None): + reindex_called[0] = True + self.assertEqual(ids, ['test_field3']) + original_reindex(ids) + + catalog.manage_reindexIndex = mock_reindex + + portal.add_catalog_indexes(test_indexes2, reindex=True) + self.assertTrue(reindex_called[0]) + + # Reset flag and test with reindex=False + reindex_called[0] = False + portal.add_catalog_indexes([('test_field4', 'FieldIndex')], reindex=False) + self.assertFalse(reindex_called[0]) + + finally: + # Restore original method + catalog.manage_reindexIndex = original_reindex + + # Test with custom logger + test_logger = logging.getLogger('test.plone.api.portal') + + with self.assertLogs('test.plone.api.portal', level='INFO') as cm: + portal.add_catalog_indexes([('test_field5', 'FieldIndex')], logger=test_logger) + + log_output = '\n'.join(cm.output) + self.assertIn('Added FieldIndex index for field test_field5', log_output) + self.assertIn('Reindexing new indexes: test_field5', log_output) + + def test_add_catalog_metadata(self): + """Test adding catalog metadata columns.""" + from plone.api.portal import add_catalog_metadata + import logging + + catalog = portal.get_tool('portal_catalog') + initial_columns = catalog.schema() + + # Test adding new columns + test_columns = ['test_col1', 'test_col2'] + + added = add_catalog_metadata(test_columns) + + # Verify columns were added + self.assertEqual(len(added), 2) + self.assertIn('test_col1', added) + self.assertIn('test_col2', added) + self.assertIn('test_col1', catalog.schema()) + self.assertIn('test_col2', catalog.schema()) + + # Test adding already existing columns + added = add_catalog_metadata(test_columns) + self.assertEqual(len(added), 0) # No new columns should be added + + # Test with custom logger + test_logger = logging.getLogger('test.plone.api.portal') + + with self.assertLogs('test.plone.api.portal', level='INFO') as cm: + add_catalog_metadata(['test_col3'], logger=test_logger) + + log_output = '\n'.join(cm.output) + self.assertIn('Added metadata column: test_col3', log_output) From 63e98a8bde5181850cbf3021f617b0cb900e2c73 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Mon, 24 Mar 2025 00:29:51 +0530 Subject: [PATCH 04/14] changelog --- news/404.feature | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 news/404.feature diff --git a/news/404.feature b/news/404.feature new file mode 100644 index 00000000..744d2306 --- /dev/null +++ b/news/404.feature @@ -0,0 +1,3 @@ +Added two new helper methods to plone.api.portal: +- add_catalog_indexes: Adds the specified indexes to portal_catalog if they don't already exist @rohnsha0 +- add_catalog_metadata: Adds the specified metadata columns to portal_catalog if they don't already exist @rohnsha0 From bbf309970fea3ac606de80835f9d7d8e16ec8aa1 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Mon, 24 Mar 2025 06:25:03 +0530 Subject: [PATCH 05/14] reformat code --- src/plone/api/tests/test_portal.py | 102 +++++++++++++++-------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/src/plone/api/tests/test_portal.py b/src/plone/api/tests/test_portal.py index d68523a4..835060cd 100644 --- a/src/plone/api/tests/test_portal.py +++ b/src/plone/api/tests/test_portal.py @@ -968,96 +968,98 @@ def test_add_catalog_indexes(self): """Test adding catalog indexes.""" import logging - catalog = portal.get_tool('portal_catalog') - initial_indexes = catalog.indexes() + catalog = portal.get_tool("portal_catalog") # Test adding new indexes test_indexes = [ - ('test_field1', 'FieldIndex'), - ('test_field2', 'KeywordIndex'), + ("test_field1", "FieldIndex"), + ("test_field2", "KeywordIndex"), ] - + added = portal.add_catalog_indexes(test_indexes) - + # Verify indexes were added self.assertEqual(len(added), 2) - self.assertIn('test_field1', added) - self.assertIn('test_field2', added) - self.assertIn('test_field1', catalog.indexes()) - self.assertIn('test_field2', catalog.indexes()) - + self.assertIn("test_field1", added) + self.assertIn("test_field2", added) + self.assertIn("test_field1", catalog.indexes()) + self.assertIn("test_field2", catalog.indexes()) + # Test adding already existing indexes added = portal.add_catalog_indexes(test_indexes) self.assertEqual(len(added), 0) # No new indexes should be added - + # Test with reindex=False test_indexes2 = [ - ('test_field3', 'FieldIndex'), + ("test_field3", "FieldIndex"), ] - + # Create a mock for catalog.manage_reindexIndex to verify it's called or not original_reindex = catalog.manage_reindexIndex - + try: reindex_called = [False] + def mock_reindex(ids=None): reindex_called[0] = True - self.assertEqual(ids, ['test_field3']) + self.assertEqual(ids, ["test_field3"]) original_reindex(ids) - + catalog.manage_reindexIndex = mock_reindex - + portal.add_catalog_indexes(test_indexes2, reindex=True) self.assertTrue(reindex_called[0]) - + # Reset flag and test with reindex=False reindex_called[0] = False - portal.add_catalog_indexes([('test_field4', 'FieldIndex')], reindex=False) + portal.add_catalog_indexes([("test_field4", "FieldIndex")], reindex=False) self.assertFalse(reindex_called[0]) - + finally: # Restore original method catalog.manage_reindexIndex = original_reindex - + # Test with custom logger - test_logger = logging.getLogger('test.plone.api.portal') - - with self.assertLogs('test.plone.api.portal', level='INFO') as cm: - portal.add_catalog_indexes([('test_field5', 'FieldIndex')], logger=test_logger) - - log_output = '\n'.join(cm.output) - self.assertIn('Added FieldIndex index for field test_field5', log_output) - self.assertIn('Reindexing new indexes: test_field5', log_output) + test_logger = logging.getLogger("test.plone.api.portal") + + with self.assertLogs("test.plone.api.portal", level="INFO") as cm: + portal.add_catalog_indexes( + [("test_field5", "FieldIndex")], logger=test_logger + ) + + log_output = "\n".join(cm.output) + self.assertIn("Added FieldIndex index for field test_field5", log_output) + self.assertIn("Reindexing new indexes: test_field5", log_output) def test_add_catalog_metadata(self): """Test adding catalog metadata columns.""" from plone.api.portal import add_catalog_metadata + import logging - - catalog = portal.get_tool('portal_catalog') - initial_columns = catalog.schema() - + + catalog = portal.get_tool("portal_catalog") + # Test adding new columns - test_columns = ['test_col1', 'test_col2'] - + test_columns = ["test_col1", "test_col2"] + added = add_catalog_metadata(test_columns) - + # Verify columns were added self.assertEqual(len(added), 2) - self.assertIn('test_col1', added) - self.assertIn('test_col2', added) - self.assertIn('test_col1', catalog.schema()) - self.assertIn('test_col2', catalog.schema()) - + self.assertIn("test_col1", added) + self.assertIn("test_col2", added) + self.assertIn("test_col1", catalog.schema()) + self.assertIn("test_col2", catalog.schema()) + # Test adding already existing columns added = add_catalog_metadata(test_columns) self.assertEqual(len(added), 0) # No new columns should be added - + # Test with custom logger - test_logger = logging.getLogger('test.plone.api.portal') - - with self.assertLogs('test.plone.api.portal', level='INFO') as cm: - add_catalog_metadata(['test_col3'], logger=test_logger) - - log_output = '\n'.join(cm.output) - self.assertIn('Added metadata column: test_col3', log_output) + test_logger = logging.getLogger("test.plone.api.portal") + + with self.assertLogs("test.plone.api.portal", level="INFO") as cm: + add_catalog_metadata(["test_col3"], logger=test_logger) + + log_output = "\n".join(cm.output) + self.assertIn("Added metadata column: test_col3", log_output) From 45e4696f00f960b218a10f9378a91d0d458d674a Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Mon, 24 Mar 2025 07:12:53 +0530 Subject: [PATCH 06/14] add docs --- docs/portal.md | 74 +++++++++++++++++++++++++++++++++++++++++ src/plone/api/portal.py | 18 +++++----- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/docs/portal.md b/docs/portal.md index 167e7b1f..e243d1e1 100644 --- a/docs/portal.md +++ b/docs/portal.md @@ -459,6 +459,80 @@ for vocabulary_name in common_vocabularies: assert vocabulary_name in vocabulary_names ``` +(portal-add-catalog-indexes-example)= + +## Add catalog indexes + +To add new indexes to the portal catalog if they don't already exist, use {meth}`api.portal.add_catalog_indexes`. + +```python +from plone import api + +# Add new indexes with default logging +indexes = [ + ('custom_field', 'FieldIndex'), + ('review_date', 'DateIndex') +] +api.portal.add_catalog_indexes(wanted_indexes=indexes) + +# Add indexes but skip reindexing +api.portal.add_catalog_indexes( + wanted_indexes=[('modified_by', 'FieldIndex')], + reindex=False +) + +# Add indexes with custom logger +import logging +custom_logger = logging.getLogger('my.package') +api.portal.add_catalog_indexes( + wanted_indexes=[('creation_user', 'FieldIndex')], + logger=custom_logger +) +``` + +% invisible-code-block: python +% +% # Verify the indexes were added to the catalog +% catalog = api.portal.get_tool('portal_catalog') +% self.assertIn('custom_field', catalog.indexes()) +% self.assertIn('review_date', catalog.indexes()) +% self.assertIn('modified_by', catalog.indexes()) +% self.assertIn('creation_user', catalog.indexes()) + +This function returns a list of the names of the indexes that were added. + +(portal-add-catalog-metadata-example)= + +## Add catalog metadata columns + +To add new metadata columns to the portal catalog if they don't already exist, use {meth}`api.portal.add_catalog_metadata`. + +```python +from plone import api + +# Add new metadata columns with default logging +columns = ['custom_metadata', 'author_email'] +api.portal.add_catalog_metadata(wanted_columns=columns) + +# Add columns with custom logger +import logging +custom_logger = logging.getLogger('my.package') +api.portal.add_catalog_metadata( + wanted_columns=['publication_date'], + logger=custom_logger +) +``` + +% invisible-code-block: python +% +% # Verify the columns were added to the catalog +% catalog = api.portal.get_tool('portal_catalog') +% self.assertIn('custom_metadata', catalog.schema()) +% self.assertIn('author_email', catalog.schema()) +% self.assertIn('publication_date', catalog.schema()) + +This function returns a list of the names of the columns that were added. Note that adding metadata columns only makes them available for storage - you still need to reindex your content to populate the values. + ## Further reading For more information on possible flags and usage options please see the full {ref}`plone-api-portal` specification. diff --git a/src/plone/api/portal.py b/src/plone/api/portal.py index 4a200065..e67a3125 100644 --- a/src/plone/api/portal.py +++ b/src/plone/api/portal.py @@ -510,15 +510,15 @@ def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): @required_parameters("wanted_columns") def add_catalog_metadata(wanted_columns, logger=None): - """ - Add the specified metadata columns to portal_catalog if they don't already exist. - - Parameters: - - wanted_columns: List of column names to add - - logger: Optional logger instance - - Returns: - - List of newly added column names + """Add the specified metadata columns to portal_catalog if they don't already exist. + + :param wanted_columns: [required] List of column names to add + :type wanted_columns: list + :param logger: Optional custom logger instance + :type logger: logging.Logger + :returns: List of names of columns that were added + :rtype: list + :Example: :ref:`portal-add-catalog-metadata-example` """ if logger is None: logger = logging.getLogger("plone.api.portal") From a6681a5077e97843bc0d10e626ee547442f2df54 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Mon, 24 Mar 2025 23:54:55 +0530 Subject: [PATCH 07/14] support ZCTextIndex with additional parameters and update documentation --- docs/portal.md | 46 ++++++++++-------------- src/plone/api/portal.py | 57 ++++++++++++++++++++++++------ src/plone/api/tests/test_portal.py | 25 +++++++++++++ 3 files changed, 91 insertions(+), 37 deletions(-) diff --git a/docs/portal.md b/docs/portal.md index e243d1e1..0de53e4f 100644 --- a/docs/portal.md +++ b/docs/portal.md @@ -468,38 +468,30 @@ To add new indexes to the portal catalog if they don't already exist, use {meth} ```python from plone import api -# Add new indexes with default logging -indexes = [ - ('custom_field', 'FieldIndex'), - ('review_date', 'DateIndex') -] -api.portal.add_catalog_indexes(wanted_indexes=indexes) +# Add a single field index +api.portal.add_catalog_indexes([('my_custom_field', 'FieldIndex')]) -# Add indexes but skip reindexing -api.portal.add_catalog_indexes( - wanted_indexes=[('modified_by', 'FieldIndex')], - reindex=False -) +# Add multiple indexes with different types +indexes_to_add = [ + ('text_content', 'ZCTextIndex'), + ('tags', 'KeywordIndex') +] +api.portal.add_catalog_indexes(indexes_to_add) -# Add indexes with custom logger -import logging -custom_logger = logging.getLogger('my.package') -api.portal.add_catalog_indexes( - wanted_indexes=[('creation_user', 'FieldIndex')], - logger=custom_logger -) +# Add indexes without reindexing +api.portal.add_catalog_indexes([('quick_field', 'FieldIndex')], reindex=False) ``` -% invisible-code-block: python -% -% # Verify the indexes were added to the catalog -% catalog = api.portal.get_tool('portal_catalog') -% self.assertIn('custom_field', catalog.indexes()) -% self.assertIn('review_date', catalog.indexes()) -% self.assertIn('modified_by', catalog.indexes()) -% self.assertIn('creation_user', catalog.indexes()) +### ZCTextIndex Special Handling + +When adding a `ZCTextIndex`, the function automatically applies additional parameters: +- `lexicon_id`: Set to 'plone_lexicon' by default +- `index_type`: Set to 'Okapi BM25 Rank' +- `doc_attr`: Set to the index name provided + +This ensures proper configuration for text-based searching and indexing in Plone. -This function returns a list of the names of the indexes that were added. +The function returns a list of the names of the indexes that were added. (portal-add-catalog-metadata-example)= diff --git a/src/plone/api/portal.py b/src/plone/api/portal.py index e67a3125..38d38f99 100644 --- a/src/plone/api/portal.py +++ b/src/plone/api/portal.py @@ -477,27 +477,64 @@ def get_vocabulary_names(): @required_parameters("wanted_indexes") def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): - """ - Add the specified indexes to portal_catalog if they don't already exist. + """Add the specified indexes to portal_catalog if they don't already exist. - Parameters: - - wanted_indexes: List of tuples in format (index_name, index_type) - - reindex: Boolean indicating if newly added indexes should be reindexed - - logger: Optional logger instance + :param wanted_indexes: [required] List of tuples in format (index_name, index_type) + :type wanted_indexes: list + :param reindex: Boolean indicating if newly added indexes should be reindexed + :type reindex: bool + :param logger: Optional logger instance + :type logger: logging.Logger + :returns: List of newly added index names + :rtype: list + :Example: :ref:`portal-add-catalog-indexes-example` - Returns: - - List of newly added index names + Note: ZCTextIndex indexes require special handling with additional parameters. + The function automatically configures lexicon_id, index_type and doc_attr + parameters when adding a ZCTextIndex and creates a minimal lexicon if needed. """ if logger is None: logger = logging.getLogger("plone.api.portal") catalog = get_tool("portal_catalog") existing_indexes = catalog.indexes() - added_indexes = [] + + # Import required classes for ZCTextIndex + from Products.ZCTextIndex.Lexicon import Lexicon + for name, meta_type in wanted_indexes: if name not in existing_indexes: - catalog.addIndex(name, meta_type) + if meta_type == "ZCTextIndex": + # Ensure a proper configuration for ZCTextIndex + extra = { + "lexicon_id": "plone_lexicon", + "index_type": "Okapi BM25 Rank", + "doc_attr": name, + } + + # Try to get the existing lexicon or create a minimal one + try: + # Try to find the lexicon in the catalog + lexicon = getattr(catalog, "plone_lexicon", None) + + # If lexicon doesn't exist, create a minimal one + if lexicon is None: + from Products.ZCTextIndex.ZCTextIndex import PLexicon + + lexicon = PLexicon("plone_lexicon", "Plone Lexicon", Lexicon()) + catalog._setObject("plone_lexicon", lexicon) + + # Add the index with the extra parameters + catalog.addIndex(name, meta_type, extra) + + except Exception as e: + logger.error(f"Error adding ZCTextIndex {name}: {str(e)}") + continue + else: + # For non-ZCTextIndex types, use standard addIndex + catalog.addIndex(name, meta_type) + added_indexes.append(name) logger.info("Added %s index for field %s.", meta_type, name) diff --git a/src/plone/api/tests/test_portal.py b/src/plone/api/tests/test_portal.py index 835060cd..78300a60 100644 --- a/src/plone/api/tests/test_portal.py +++ b/src/plone/api/tests/test_portal.py @@ -1031,6 +1031,31 @@ def mock_reindex(ids=None): self.assertIn("Added FieldIndex index for field test_field5", log_output) self.assertIn("Reindexing new indexes: test_field5", log_output) + def test_add_catalog_indexes_zctext_index(self): + """Test adding a ZCTextIndex type index with appropriate extra parameters.""" + from plone.api import portal + + # Mock the catalog + catalog_mock = mock.Mock() + catalog_mock.indexes = mock.Mock(return_value=[]) + + # Replace the get_tool function to return our mock + with mock.patch.object(portal, "get_tool", return_value=catalog_mock): + # Call the function with a ZCTextIndex + portal.add_catalog_indexes([("myindex", "ZCTextIndex")], reindex=False) + + # Verify that addIndex was called with the correct extra parameters + catalog_mock.addIndex.assert_called_once() + name, meta_type, extra = catalog_mock.addIndex.call_args[0] + self.assertEqual(name, "myindex") + self.assertEqual(meta_type, "ZCTextIndex") + self.assertEqual(extra["lexicon_id"], "plone_lexicon") + self.assertEqual(extra["index_type"], "Okapi BM25 Rank") + self.assertEqual(extra["doc_attr"], "myindex") + + # Verify that manage_reindexIndex wasn't called (reindex=False) + catalog_mock.manage_reindexIndex.assert_not_called() + def test_add_catalog_metadata(self): """Test adding catalog metadata columns.""" from plone.api.portal import add_catalog_metadata From 757f94bbf2ac857e3eed3d20d587a4b4cdcaf595 Mon Sep 17 00:00:00 2001 From: Rohan Shaw <86848116+rohnsha0@users.noreply.github.com> Date: Mon, 24 Mar 2025 23:58:05 +0530 Subject: [PATCH 08/14] Apply suggestions from code review Co-authored-by: Steve Piercy --- docs/portal.md | 16 +++++++++++++--- src/plone/api/portal.py | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/portal.md b/docs/portal.md index 0de53e4f..6012e4bd 100644 --- a/docs/portal.md +++ b/docs/portal.md @@ -463,7 +463,10 @@ for vocabulary_name in common_vocabularies: ## Add catalog indexes -To add new indexes to the portal catalog if they don't already exist, use {meth}`api.portal.add_catalog_indexes`. +To add indexes to the portal catalog, use {meth}`api.portal.add_catalog_indexes`. +This function returns a list of the names of the indexes that were added. + +The following collection of code snippets demonstrate how to add indexes and either use default logging, to skip reindexing, or to use a customer logger. ```python from plone import api @@ -497,7 +500,15 @@ The function returns a list of the names of the indexes that were added. ## Add catalog metadata columns -To add new metadata columns to the portal catalog if they don't already exist, use {meth}`api.portal.add_catalog_metadata`. +To add metadata columns to the portal catalog, use {meth}`api.portal.add_catalog_metadata`. +This function returns a list of the names of the columns that were added. + +```{note} +Adding metadata columns only makes them available for storage. +You still need to reindex your content to populate the values. +``` + +The following collection of code snippets adds metadata columns with either the default logger or a custom logger. ```python from plone import api @@ -523,7 +534,6 @@ api.portal.add_catalog_metadata( % self.assertIn('author_email', catalog.schema()) % self.assertIn('publication_date', catalog.schema()) -This function returns a list of the names of the columns that were added. Note that adding metadata columns only makes them available for storage - you still need to reindex your content to populate the values. ## Further reading diff --git a/src/plone/api/portal.py b/src/plone/api/portal.py index 38d38f99..e0c81ce8 100644 --- a/src/plone/api/portal.py +++ b/src/plone/api/portal.py @@ -547,7 +547,7 @@ def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): @required_parameters("wanted_columns") def add_catalog_metadata(wanted_columns, logger=None): - """Add the specified metadata columns to portal_catalog if they don't already exist. + """Add the specified metadata columns to portal_catalog. :param wanted_columns: [required] List of column names to add :type wanted_columns: list From 6ba1bed5e446f7866f5898ca9d2d7846be94ac84 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Tue, 25 Mar 2025 00:10:16 +0530 Subject: [PATCH 09/14] Add Products.ZCTextIndex to install_requires in setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 4ac99259..da686d8c 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ python_requires=">=3.8", install_requires=[ "Acquisition", + "Products.ZCTextIndex", "Products.statusmessages", "Products.PlonePAS", "Products.CMFPlone", From 6456531de1d62235a65fc1adfb1fbc2334370e09 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Tue, 25 Mar 2025 00:10:40 +0530 Subject: [PATCH 10/14] add methods to doc/index page --- docs/api/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api/index.md b/docs/api/index.md index 4a8727ae..4390413e 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -39,6 +39,8 @@ exceptions api.portal.send_email api.portal.show_message api.portal.get_registry_record + api.portal.add_catalog_indexes + api.portal.add_catalog_metadata ``` From 835e325d879f3d432871af26d0d51d5e9fcdedbe Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Tue, 25 Mar 2025 05:50:06 +0530 Subject: [PATCH 11/14] add docstrings --- docs/portal.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/portal.md b/docs/portal.md index 6012e4bd..1f439aec 100644 --- a/docs/portal.md +++ b/docs/portal.md @@ -484,6 +484,14 @@ api.portal.add_catalog_indexes(indexes_to_add) # Add indexes without reindexing api.portal.add_catalog_indexes([('quick_field', 'FieldIndex')], reindex=False) ``` +% invisible-code-block: python +% +% # Verify the indexes were added to the catalog +% catalog = api.portal.get_tool('portal_catalog') +% self.assertIn('my_custom_field', catalog.indexes()) +% self.assertIn('tags', catalog.indexes()) +% self.assertIn('quick_field', catalog.indexes()) + ### ZCTextIndex Special Handling From fe845bf4b93857c048bac5f8555e7e5fea423596 Mon Sep 17 00:00:00 2001 From: Rohan Shaw <86848116+rohnsha0@users.noreply.github.com> Date: Tue, 25 Mar 2025 05:56:33 +0530 Subject: [PATCH 12/14] Apply suggestions from code review Co-authored-by: Steve Piercy --- docs/portal.md | 5 +++-- src/plone/api/portal.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/portal.md b/docs/portal.md index 1f439aec..aee87b54 100644 --- a/docs/portal.md +++ b/docs/portal.md @@ -496,8 +496,9 @@ api.portal.add_catalog_indexes([('quick_field', 'FieldIndex')], reindex=False) ### ZCTextIndex Special Handling When adding a `ZCTextIndex`, the function automatically applies additional parameters: -- `lexicon_id`: Set to 'plone_lexicon' by default -- `index_type`: Set to 'Okapi BM25 Rank' + +- `lexicon_id`: Set to `plone_lexicon` by default +- `index_type`: Set to `Okapi BM25 Rank` - `doc_attr`: Set to the index name provided This ensures proper configuration for text-based searching and indexing in Plone. diff --git a/src/plone/api/portal.py b/src/plone/api/portal.py index e0c81ce8..dae0f08f 100644 --- a/src/plone/api/portal.py +++ b/src/plone/api/portal.py @@ -490,7 +490,7 @@ def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): :Example: :ref:`portal-add-catalog-indexes-example` Note: ZCTextIndex indexes require special handling with additional parameters. - The function automatically configures lexicon_id, index_type and doc_attr + The function automatically configures lexicon_id, index_type, and doc_attr parameters when adding a ZCTextIndex and creates a minimal lexicon if needed. """ if logger is None: From df7dc430d9137ff94ea16b72aa5ecf5dc97b3527 Mon Sep 17 00:00:00 2001 From: Rohan Shaw Date: Tue, 25 Mar 2025 06:22:11 +0530 Subject: [PATCH 13/14] rename parameters in add_catalog_indexes and add_catalog_metadata for clarity --- docs/portal.md | 4 ++-- src/plone/api/portal.py | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/portal.md b/docs/portal.md index aee87b54..9110e44a 100644 --- a/docs/portal.md +++ b/docs/portal.md @@ -524,13 +524,13 @@ from plone import api # Add new metadata columns with default logging columns = ['custom_metadata', 'author_email'] -api.portal.add_catalog_metadata(wanted_columns=columns) +api.portal.add_catalog_metadata(columns_to_add=columns) # Add columns with custom logger import logging custom_logger = logging.getLogger('my.package') api.portal.add_catalog_metadata( - wanted_columns=['publication_date'], + columns_to_add=['publication_date'], logger=custom_logger ) ``` diff --git a/src/plone/api/portal.py b/src/plone/api/portal.py index dae0f08f..3502a314 100644 --- a/src/plone/api/portal.py +++ b/src/plone/api/portal.py @@ -475,12 +475,12 @@ def get_vocabulary_names(): return sorted([name for name, vocabulary in getUtilitiesFor(IVocabularyFactory)]) -@required_parameters("wanted_indexes") -def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): +@required_parameters("indexes_to_add") +def add_catalog_indexes(indexes_to_add, reindex=True, logger=None): """Add the specified indexes to portal_catalog if they don't already exist. - :param wanted_indexes: [required] List of tuples in format (index_name, index_type) - :type wanted_indexes: list + :param indexes_to_add: [required] List of tuples in format (index_name, index_type) + :type indexes_to_add: list :param reindex: Boolean indicating if newly added indexes should be reindexed :type reindex: bool :param logger: Optional logger instance @@ -503,7 +503,7 @@ def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): # Import required classes for ZCTextIndex from Products.ZCTextIndex.Lexicon import Lexicon - for name, meta_type in wanted_indexes: + for name, meta_type in indexes_to_add: if name not in existing_indexes: if meta_type == "ZCTextIndex": # Ensure a proper configuration for ZCTextIndex @@ -545,12 +545,12 @@ def add_catalog_indexes(wanted_indexes, reindex=True, logger=None): return added_indexes -@required_parameters("wanted_columns") -def add_catalog_metadata(wanted_columns, logger=None): +@required_parameters("columns_to_add") +def add_catalog_metadata(columns_to_add, logger=None): """Add the specified metadata columns to portal_catalog. - :param wanted_columns: [required] List of column names to add - :type wanted_columns: list + :param columns_to_add: [required] List of column names to add + :type columns_to_add: list :param logger: Optional custom logger instance :type logger: logging.Logger :returns: List of names of columns that were added @@ -564,7 +564,7 @@ def add_catalog_metadata(wanted_columns, logger=None): existing_columns = catalog.schema() added_columns = [] - for name in wanted_columns: + for name in columns_to_add: if name not in existing_columns: catalog.addColumn(name) added_columns.append(name) From 600879493507401ad99055d98f0d19a4843c7190 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 24 Mar 2025 23:38:32 -0700 Subject: [PATCH 14/14] Update docs/portal.md --- docs/portal.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/portal.md b/docs/portal.md index 9110e44a..86536e2e 100644 --- a/docs/portal.md +++ b/docs/portal.md @@ -484,6 +484,7 @@ api.portal.add_catalog_indexes(indexes_to_add) # Add indexes without reindexing api.portal.add_catalog_indexes([('quick_field', 'FieldIndex')], reindex=False) ``` + % invisible-code-block: python % % # Verify the indexes were added to the catalog