Making a custom catalog for a specific content type

I'm trying to make a custom catalog for a specific content type. I was trying to follow this:
s17.person
as an example, but I'm missing something that is allowing content to actually reindex itself.

/content/MyObject.py:

class IMyObject(Interface):

    #Title and description provided by IBasic    

    Model_Number = schema.TextLine(title=u"",
                                  required=False,
                                 )

    Serial_Number = schema.TextLine(title=u"",
                                    required = False
                                 )

@implementer(IMyObject)
class MyObject(Container):
    
    def _catalogs(self):
        return [
                   getToolByName(self, 'portal_myobjectcatalog')
                ]

    def indexObject(self):
        for c in self._catalogs:
            c.catalog_object(self)

    def unindexObject(self):
        ''' remove an object from all registered catalogs '''
        path = '/'.join(self.getPhysicalPath())
        for c in self._catalogs():
            c.uncatalog_object(path)

    def reindexObjectSecurity(self, skip_self=False):
        path = '/'.join(self.getPhysicalPath())
        for c in self._catalogs():
            for brain in c.unrestrictedSearchResults(path=path):
                brain_path = brain.getPath()
                if brain_path == path and skip_self:
                    continue
            # Get the object
            ob = brain._unrestrictedGetObject()

            # Recatalog with the same catalog uid.
            # _cmf_security_indexes in CMFCatalogAware
            c.reindexObject(ob,
                            idxs=self._cmf_security_indexes,
                            update_metadata=0,
                            uid=brain_path
                            )

    def reindexObject(self, idxs=[]):
        ''' reindex object '''
        if idxs == []:
            # Update the modification date.
            if hasattr(aq_base(self), 'notifyModified'):
               self.notifyModified()
        for c in self._catalogs():
            if c is not None:
                c.reindexObject(self,
                                idxs=idxs
                                )

/profiles/default/myobjectcatalog.xml

<?xml version="1.0"?>
<object name="portal_myobjectcatalog" meta_type="MyObjectCatalog">
  <property name="title">Index MyObject content on a site</property>
  <property name="unwrapobjects" type="boolean">True</property>
  .... lexicons
  .... default plone fields

  <!-- MyObject Related -->

  <index name="Model_Number" meta_type="ZCTextIndex">
    <indexed_attr value="Model_Number"/>
    <extra name="index_type" value="Okapi BM25 Rank"/>
    <extra name="lexicon_id" value="plone_lexicon"/>
  </index>

  <index name="Serial_Number" meta_type="ZCTextIndex">
    <indexed_attr value="Serial_Number"/>
    <extra name="index_type" value="Okapi BM25 Rank"/>
    <extra name="lexicon_id" value="plone_lexicon"/>
  </index>

</object>

/catalog.py

class IMyObjectCatalog(Interface):
    """A specialized catalog to deal with MyObjects
    """

class MyObjectCatalog(CatalogTool):
    """A specialized catalog to deal with MyObjects
    """

    id = 'portal_myobjectcatalog'
    portal_type = meta_type = 'MyObjectCatalog'
    implements(IMyObjectCatalog)

    def getId(self):
        return self.id

InitializeClass(MyObjectCatalog)

/exportimport/catalog.py

class MyObjecttCatalogXMLAdapter(ZCatalogXMLAdapter):

    __used_for__ = IMyObjectCatalog
    adapts(IMyObjectCatalog, ISetupEnviron)
    name = 'myobjectcatalog'

    def _exportNode(self):
        """
        Export the settings as a DOM node.
        """
        node = ZCatalogXMLAdapter._exportNode(self)

        self._logger.info('My Object Catalog settings exported.')
        return node

    def _importNode(self, node):
        """
       Import the settings from the DOM node.
        """
        ZCatalogXMLAdapter._importNode(self, node)

        self._logger.info('My Object Catalog settings imported.')

def importMyObjectCatalog(context):
    """
    Import portal_myobjectcatalog configuration.
    """

    site = context.getSite()
    tool = getToolByName(site, 'portal_myobjectcatalog', None)

    if tool is None:
        logger = context.getLogger("my.product")
        logger.info("Catalog not installed.")
        return

    importObjects(tool, '', context)


def exportMyObjectCatalog(context):
    """
    Export portal_myobjectcatalog configuration.
    """

    site = context.getSite()
    tool = getToolByName(site, 'portal_myobjectcatalog', None)
    if tool is None:
        logger = context.getLogger("my.product")
        logger.info("Nothing to export.")
        return

    exportObjects(tool, '', context)

/configure.zcml

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
    xmlns:i18n="http://namespaces.zope.org/i18n"
    xmlns:plone="http://namespaces.plone.org/plone"
    i18n_domain="my.object">

    ... Below <genericsetup:registerProfile>
   <genericsetup:importStep
        name="my.product "
        title="My Object Import Step"
        description=""
        handler="my.object.exportimport.catalog.importMyObjectCatalog">
            <depends name="toolset"/>
  </genericsetup:importStep>

  <genericsetup:exportStep
      name="my.product"
      title="My Object Catalog Export Step"
      description=""
      handler="my.product.exportimport.catalog.exportMyObjectCatalog"
   />    
   ....
   <adapter factory=".exportimport.catalog.MyObjectCatalogXMLAdapter"/>  

</configure>

/profiles/default/toolset.xml

<?xml version="1.0"?>
<tool-setup>
     <required tool_id="portal_myobjectcatalog"
               class="my.product.catalog.MyObjectCatalog"/>
</tool-setup>

When I check the myobject catalog, the data for a new MyObject is there, but its a bit weird. When I go to the ZMI and select portal_myobjcatalog, an instance of MyObject I created said under the 'Type' column that it was 'Unknown'. Also, when I go to the indexes tab and select one of the indexes, there is a link that says ZCTextIndex Lexicon used. The link is incorrect and takes me to a Not Found page.

The main problem I'm having is that the catalog won't reindex the object when its been modified. ReindexObject is called by reindexOnModify, which only triggers if a field has been modified, but the indexes parameter (idxs) is empty and myobject catalog doesn't reindex the object that was modified.

I'm using Plone 5.1 and while s17.person uses grok, I am not using it.

What am I doing wrong that could cause this not to work properly?

ZCTextIndex is likely not the type of index you need, a FieldIndex would be better.

Also, what are your reasons for creating a whole new catalog? Using the default catalog should be sufficient.

1 Like

Thank you for your response.
I did change it to FieldIndex. The link issue I mentioned isn't there anymore.
Unfortunately, when I modify a field and save, it still doesn't reindex.

We're making a catalog because we're essentially trying to make a focused search for MyObject used by a content type that manages MyObjects.

You can get this with the standard catalog. If you want to hide your MyObject CT from the default site search, you can disable it for that. And your custom focused search can use the default catalog.

1 Like

I see. Thank you.
I am curious though why my catalog isn't working though. Are there components that s17 is using that I might not have?

That is not a surprise. In order for an object to reindex itself, it must know in which catalogs it must do the reindex.

The former Archetypes content framework (used by default until Plone 4) had a mixin class CatalogMultiplex which was responsible to perform index operations on all known catalogs. Formerly, Plone used more than a single one: besides the default catalog, there has also been an "uid catalog" and a "reference_catalog" -- all those special purpose catalogs have now been merged into the standard catalog. As far as I know, the Archetypes successor (dexterity) no longer has this mixin class - and therefore, objects only know about the default catalog.

I agree with Thomas that you should be able to realize your requirements with the standard catalog. Otherwise, you will need to enhance your content type that its indexing operations know about more the the default catalog - along the line of the CatalogMixin from Archetypes.

1 Like