Indexer post processor hook in Liferay 6.1

Hooks are a feature to catch hold of the properties and JSP files into an instance of the portal, as if catching them with a hook. Hook plugins are more powerful plugins that come to complement portlets, themes, layout templates, and web modules. A hook plugin can, but does not have to, be combined with a portlet plugin or a web plugin. For instance, the portlet called so-portlet is a portlet plugin for Social Office with hooks; a hook plugin can simply provide translation or override JSP page. In general, hooks would be very helpful tool to customize the portal without touching the code part of the portal, as shown in the following diagram. In addition, you could use hooks to provide patches for portal systems or social office products.

In general, there are several kinds of hook parameters in order:

  • portal-properties (called portal properties hooks),
  • language-properties (called language properties hooks),
  • custom-jsp-dir (called custom JSPs hooks),
  • custom-jsp-global (applying custom JSPs hooks globally or locally),
  • indexer post processors (called indexer hook),
  • service (called portal service hooks) – including model listeners and service wrappers,
  • servlet-filter and servlet-filter-mapping (called servlet-filter hooks),
  • struts-action (called portal struts action hooks)

As you can see, JSPs hooks can set a custom-jsp-dir that will overwrite portal JSPs. You can also add <custom-jsp-global>false</custom-jsp-global> (default to true) so that JSPs hooks will not apply globally but only to the current scope. Each site (or organization) can choose to have that hook apply just for that site (or organization).

In addition, Liferay allows portal JSPs to be overloaded by theme templates – this pattern will require that within the theme's templates folder the complete path to the original JSP be maintained with the file extension replaced to match that of the theme's chosen template language.

Abstracted from the Liferay development cookbook: Liferay Portal Systems Development (for liferay portal 6.1 or above version)

The indexer hook allows building a post processing system on top of the existing indexer, therefore plugin hook developers could be able to modify the search summaries, indexes, and queries. Refer to LPS-15811.

This article will show how to build sample indexer post processor hook. Let’s consider use case. As you know, the version 6.1 adds a lot of CMS (Documents and Media) features like

  • Capability to manage different document types: basic document, image (Image Gallery is merged into Document Library), video, audio, etc.
  • Providing Dynamic Data List (DDL) and Dynamic Data Mapping (DDM) - see blogs Dynamic Data Lists I and Dynamic Data Lists II

For the document type “Image”, we are going to add searchable keyword “Image”; while for the document type “Video”, we are going to add searchable keyword “Video”. Thus whenever you search by keyword “Image” or “Video”, you would be able to find out documents from document type “Image” or “Video”.

Implementation

You can build sample indexer post processor hook plugin in following steps.

  • Create a hook project, for example, sample-indexer-post-processor-hook
  • In liferay-hook.xml, add following lines

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_1_0.dtd">
<hook>
    <portal-properties>portal.properties</portal-properties>
    <indexer-post-processor>
        <indexer-class-name>com.liferay.portlet.documentlibrary.model.DLFileEntry</indexer-class-name>
        <indexer-post-processor-impl>com.liferay.hook.indexer.SampleIndexerPostProcessor</indexer-post-processor-impl>
    </indexer-post-processor>
</hook>

As you can see, you can set indexer-class-name as a model entity like DLFileEntry: com.liferay.portlet.documentlibrary.model.DLFileEntry; and set the indexer-post-processor-impl as com.liferay.hook.indexer.SampleIndexerPostProcessor, which must implement the interface com.liferay.portal.kernel.search.IndexerPostProcessor.

  • In web.xml, add following lines

<web-app>
    <display-name>sample-indexer-post-processor-hook</display-name>
    <listener>
        <listener-class>com.liferay.portal.kernel.servlet.SerializableSessionAttributeListener</listener-class>
    </listener>
    <listener>
        <listener-class>com.liferay.portal.kernel.servlet.HookContextListener</listener-class>
    </listener>
</web-app>

  • Create sample indexer post processor class: com.liferay.hook.indexer.SampleIndexerPostProcessor

public class SampleIndexerPostProcessor implements IndexerPostProcessor
{
    public void postProcessContextQuery(BooleanQuery arg0, SearchContext arg1)
            throws Exception {       
        if(_log.isDebugEnabled())
            _log.debug("called postProcessContextQuery()");       
    }

    public void postProcessDocument(Document document, Object object)
            throws Exception {       
        DLFileEntry dLFileEntry = (DLFileEntry) object;       
        if(_log.isDebugEnabled())
            _log.debug("called postProcessDocument(): uuid=" + dLFileEntry.getUuid() +
                    " title=" + dLFileEntry.getTitle() +
                    " dLFileEntryId=" + dLFileEntry.getFileEntryId());       
        String text = "";       
        try {           
            long fileEntryTypeId = dLFileEntry.getFileEntryTypeId();           
            DLFileEntryType dLFileEntryType = DLFileEntryTypeLocalServiceUtil.getDLFileEntryType(fileEntryTypeId);
             // add your logic here
            text = dLFileEntryType.getName();           
        } catch (Exception e) {}       
        if(text.length() > 0)
            document.addText(Field.URL, text);       
    }

    public void postProcessFullQuery(BooleanQuery arg0, SearchContext arg1)
            throws Exception {       
        if(_log.isDebugEnabled())
            _log.debug("called postProcessFullQuery()");       
    }

    public void postProcessSearchQuery(BooleanQuery arg0, SearchContext arg1)
            throws Exception {       
        if(_log.isDebugEnabled())
            _log.debug("called postProcessSearchQuery()");       
    }
    public void postProcessSummary(Summary arg0, Document arg1, Locale arg2,
            String arg3, PortletURL arg4) {       
        if(_log.isDebugEnabled())
            _log.debug("postProcessSummary()");       
    }   
    private static Log _log = LogFactoryUtil.getLog(SampleIndexerPostProcessor.class);
}

As shown in the above code, you would be able to add your own logic for summary, search query, context query and document. That’s it.

Results

search by keyword "Image"

Search by keyword "Video"

Download URL

Sample indexer post processor hook Liferay 6.1 with source code.

What’s next?

The next post would be: sample servlet-filter and servlet-filter-mapping hook in Liferay 6.1.
 

Blogs
Really cool feature, as we can do indexing through hooks (not only indexing though)

Not able to download hook attached.

Getting following error.

You do not have permission to access the requested resource.

http://www.liferay.com/documents/31578/11633350/sample-indexer-post-processor-hook-6.1.0.1.war
I tried after logged into Liferay only - Not able to download
Hi Sandeep, good point. Ideally, the deploy process should automatically add these lines in the web.xml.
Hi Tomas, this feature is not available in the 5.2 and 6.0 yet. Hope that this could be back-ported in the near future.

Thanks

Jonas
Hi Nagendra,

It is weird. The "VIEW" permission was assigned to the "Guest". Would you be able to download anything from the site?

Thanks

Jonas
Nice Article. Thanks for your detailed explanation Jonus.

Like Nagendra, I am also getting that same error while trying to download this war file: "You do not have permission to access the requested resource."
Hi Nagendra & Zubair, please use teh following link

http://www.liferay.com/documents/31578/6144150/sample-jquery-portlet-6.1.0.1.war
Hi Jonas Yuan,
Is ther any way to index related discussions with the document entry?
The indexer post processor hook is great. There is one issue in my case:
I try to post process the DLFileEntry to index the discussions related to the documents, but it doesn't work while adding comments does not update the file entry itself.
Is there any way to achieve that?

Thanks
Hello,
I've just solved my problem by using a ModelListener for MBMessage.
onAfterCreate I reindex the DLEntry and then the post processor hook take over to add discussions to my index.

Regards,
Alireza
Hi,

¿Does this also work when using the Solr plugin?

Thanks.
Hi Jonas, thanks for the useful blog. I used it to add metadata information extracted from a document type to its properties field. By doing so we want to be able to search for the document using not only its title and/or description but also i.e. the type of document information. So far, using the Solr plugin and cheking its index, we have seen that the metadata are added to the properties filed of the document although we are not able to search the document with these metadata yet. Are we missing something else to be done?, Should some service be extended in terms of a hook?

We would appreciate any information someone could provide us with on this matter.

Thank you very much.
HI,

I want to filter users from search result on country which I select from selectbox.
How can I use index post processor for this.

Can you give example?
Hi Jonas,

Very nice article.I have one query can we index other models for which the indexer is not already defined. E.g if we want to index the Address model, can that be achieved using this approach ?