« Back to Development

Adding search capabilities to a portlet

This article needs updating. For more information, see Wiki - Need Updating.


This article shows how to add search capabilities to a portlet.

Example - Bookmark Portlet #

1. Implement a com.liferay.portal.kernel.search.Indexer class, like this one: http://lportal.svn.sourceforge.net/viewvc/lportal/portal/trunk/portal-impl/src/com/liferay/portlet/bookmarks/util/Indexer.java?view=markup

This class is responsible for adding/updating/deleting documents to the index, it uses SearchEngineUtil which is an abstraction to the underlying search engine that is being used (currently you can use Solr or Lucene, Lucene is the default engine).

When adding a document you create a Document instance and pass it to SearchEngineUtil.addDocument along with the companyId. A com.liferay.portal.kernel.search.Document is just a collection of Fields, a com.liferay.portal.kernel.search.Field has a name and a value which is the content that you want to search for, for example, a Bookmark entry has a name "This is a Bruno bookmark entry.", later on, you can search for it with this query: name:"bruno".

A call to document.addText will add a Field to the document and its value will be filtered before indexed. All words that don't help with to bring relevant search results will be removed (like punctuation, pronouns, adverbs), in the example above the bookmark entry name would become "bruno bookmark entry". addText is usually called when the content to be indexed is a long text.

A call to document.addKeyword will add a Field to the document and its value will be indexed with no modification.

document.addUID is very important because it's used to create a unique identifier to the document in the index. You will need it to update or delete the document later. There are some other useful methods to Document, like addFile, that extracts contents from many file formats, and addModifiedDate which adds the current Date as a field to the document.

Some fields are commonly used: groupId, portletId, companyId, they are useful because you can narrow your searches to only show documents that were created in a certain community or company or even a specific portlet instance.

Indexer.getDocumentSummary is used by the Search portlet to aggregate all results from all portlets in just one place, DocumentSummary will be used to render each document in the search result listing.

2. Add a <indexer-class> to liferay-portlet.xml pointing to this class. Whenever you hit the button Re-index the Indexer re-index method will be called.

3. Whenever a Bookmark entry is added/updated/deleted from the database the Indexer is called to update the index accordingly. Search for Indexer at: http://lportal.svn.sourceforge.net/viewvc/lportal/portal/trunk/portal-impl/src/com/liferay/portlet/bookmarks/service/impl/BookmarksEntryLocalServiceImpl.java?view=markup

Observe the places where Indexer is called:

Indexer.addEntry(entry.getCompanyId(), folder.getGroupId(), folderId, entryId, name, url, comments, tagsEntries); Indexer.deleteEntry(entry.getCompanyId(), entry.getEntryId()); Indexer.updateEntry(entry.getCompanyId(), folder.getGroupId(), entry.getFolderId(), entry.getEntryId(), name, url, comments, tagsEntries);

If you encounter a NullPointerException while retrieving your portlet's indexer (portlet.getIndexerInstance() returning null), check the portletId returned by YourIndexer.getPortletId() and keep in mind the portletId outside the portal will be "<portlet-name in portlet.xml>_WAR_<webapp name>"

4. Now that Bookmark entries can be added/updated and removed from the index you are able to make searches requests to SearchEngineUtil, take a look at: http://lportal.svn.sourceforge.net/viewvc/lportal/portal/trunk/portal-impl/src/com/liferay/portlet/bookmarks/service/impl/BookmarksFolderLocalServiceImpl.java?view=markup

Look at the search method: public Hits search(long companyId, long groupId, long[] folderIds, String keywords, int start, int end)

It constructs a com.liferay.portal.kernel.search.Query instance and calls SearchEngineUtil.search(companyId, fullQuery, start, end) which returns a Hits instance.

A BooleanQuery is one implementation of Query, you can specify which field values MUST (AND operator), SHOULD (OR operator) or MUST_NOT (NOT AND operator) occurrences in the results. You can create composite BooleanQueries by adding one to another.

Start and end parameters are used to paginate the results, for example if start == 0 and end == 2, only the first two entries will appear in the result, if both are equal to QueryUtil.ALL_POS (-1) than all results will be returned.

5. Now that you have the search method you can use it in a jsp, for example: http://lportal.svn.sourceforge.net/viewvc/lportal/portal/trunk/portal-web/docroot/html/portlet/bookmarks/search.jsp?view=markup

This jsp calls the search method and iterates over com.liferay.portal.kernel.search.Hits which is a collection of Documents and represents the result of the search. The Hits class gives the score (the importance) of each document, tells you how long the search took and how many documents were found.

On each Document you can get the values of each field by calling document.get(String fieldName) or getValues in case the Field has multiples stored values.

6. If you want that your portlet will be listed in the Search portlet results you need to do one more step, create a com.liferay.portal.kernel.search.OpeanSearch implementation and add a <open-search-class> to your liferay-portlet.xml. Take a look at how this is implemented for Bookmark portlet:


With this implementation you will also be able to expose search results in the OpenSearch standard format which can be interpreted by many OpenSearch clients, for more information, go to: http://www.opensearch.org

0 Attachments
Average (2 Votes)
The average rating is 4.5 stars out of 5.
Threaded Replies Author Date
Will it search only "Bruno" what will be the... Ankur Bhargava February 9, 2009 6:11 AM
Are there any more updated documents/examples... Aaron Daubman October 29, 2010 10:47 AM
Succesfuly done it in a portlet. Thanks for the... Sergey Stepanenko April 8, 2011 5:03 AM
Great article helped me a lot .. Thanks a lot GAURAV RAWAT January 6, 2012 10:57 PM
poxa, que artigo util, o exemplo entao!!... ricardo wolosker January 11, 2012 5:39 AM
I am trying to find out how to index entities... Beate Biller February 22, 2012 6:25 AM
I'm with Sergey on this. Does anyone have a... Lee PM March 23, 2012 12:28 PM
Hi i am struggling with point 6, more precisely... Botty Dimanov June 21, 2013 6:38 AM

Will it search only "Bruno" what will be the results when we search "Bruno bookmark" ?
Posted on 2/9/09 6:11 AM.
Are there any more updated documents/examples for this. I'm finding this guide difficult to follow for Liferay 6.0EE and Solr 1.4.1 with solr-web-
Posted on 10/29/10 10:47 AM.
Succesfuly done it in a portlet. Thanks for the info.

However I cannot figure out how to work with opensearch - it does work and displays results, but summary link misses p_p_auth token while other assets do have it. Hence the link from opensearch resolver into permission error for portlet.
Posted on 4/8/11 5:03 AM.
Great article helped me a lot .. Thanks a lot
Posted on 1/6/12 10:57 PM.
poxa, que artigo util, o exemplo entao!! Fantastico, Muito bom. Quando é o curso? R$ R$ R$ R$ R$ R$ R$ R$ R$ R$ R$ R$ R$ R$ R$
Posted on 1/11/12 5:39 AM in reply to GAURAV RAWAT.
I am trying to find out how to index entities with a one-to-many relationship. Does anyone have an example for this?
Posted on 2/22/12 6:25 AM.
I'm with Sergey on this. Does anyone have a good resource on opensearch basics?
I just want to add a simple external site, say yahoo through opensearch and have results in my CE 6.0/6.1 site. Does there exist a tutorial/howto for this? Thanks in advance.
Posted on 3/23/12 12:28 PM in reply to Beate Biller.
Hi i am struggling with point 6, more precisely where should the search path of OpenSearch point to?
Posted on 6/21/13 6:38 AM.