« Back

Faceted Search and Customized Filtering

General Blogs September 4, 2013 By Ray Augé Staff

Ever since the Faceted Search API came out there have been a ton of great questions about how to go about creating very specific filters.

A recent one is: "find both journal-articles and only PDF-files from Documents and Media Library"

This kind of requirement is not suited to being implemented as Facets.

Facets are "metrics" calculated across an entire result set. As such, using Facet's ability to perform drill down as a means of "filtering" will likely lead to poor performance and overly complex facet configurations.

However, there is an API available for doing precisely this type of filtering.

Unfortunately, there isn't currently a way to configure this available from any UI (Marketplace opportunity??).

 

The com.liferay.portal.kernel.search.SearchContext class has a method:

public void setBooleanClauses(BooleanClause[] booleanClauses)

 

With this method you can pass an arbitrary number of filter criteria as an array of boolean clauses.

Here is an example which supports the requirements describe above ("find both journal-articles and only PDF-files from Documents and Media Library"):

Query stringQuery = StringQueryFactoryUtil.create("entryClassName:com.liferay.portlet.journal.model.JournalArticle (+entryClassName:com.liferay.portlet.documentlibrary.model.DLFileEntry +extension:pdf)");

BooleanClause clause = BooleanClauseFactoryUtil.create(searchContext, stringQuery, BooleanClauseOccur.SHOULD.toString());

searchContext.setBooleanClauses(new BooleanClause[] {clause});

Filtering implemented in this way is several times more efficient than anything done via the Facet API. 

Another advantage of this API is support for things like exclusions "(-field:not_this_value)" which you can't do with Facets at all (you can only specify limited values to be included in facet calculations).

Lastly, I mentioned that this isn't available from the UI, but as you can see it would be extremely simple to add an advanced configuration option to the Search Portlet to store a string version of the filterQuery, enabling the filter to be set per search portlet instance.

The code might look like this:

init.jsp:

String filterQuery = portletPreferences.getValue("filterQuery", StringPool.BLANK);

main_search.jsp:

if (Validator.isNotNull(filterQuery)) {
    Query stringQuery = StringQueryFactoryUtil.create(filterQuery);

    BooleanClause clause = BooleanClauseFactoryUtil.create(searchContext, stringQuery, BooleanClauseOccur.SHOULD.toString());

    searchContext.setBooleanClauses(new BooleanClause[] {clause});
}

configuration.jsp:

<div ..="" class="advanced-configuration ..">
	.. 
    <aui:input cssclass="filter-query-text" helpmessage="filter-query-help" name="preferences--filterQuery--" type="textarea" value="<%= filterQuery %>" />
</div>

That's it!

Threaded Replies Author Date
Thanks for the quick response! It works great!... Bert Godon September 5, 2013 4:52 AM
Thanks for the blog article and your insight.... Vanita Chawla September 18, 2013 7:47 AM
Thanks for nice information, I just want to... Muradali Hasan January 23, 2014 5:38 PM
For anyone interested in this functionality... Justyna Gralinska February 12, 2014 1:34 PM
I'm glad to find something written about this... Joseph Toman August 5, 2014 4:23 PM
Hello, Could you please complete the above code... Giannis Foufas August 18, 2014 2:32 AM

Thanks for the quick response! It works great! But I have one little comment: there is no toString method on the BooleanClauseOccur object, but there is a getName method.
I used BooleanClauseOccur.MUST.getName() instead of BooleanClauseOccur.SHOULD.toString().
Posted on 9/5/13 4:52 AM.
Thanks for the blog article and your insight. It would be great if the search portlet could be configured from the portal-ext.properties or the control panel GUI at a global scope/context and per site basis. This requirement to configure search is recurring theme in our business requirements as we do more with Liferay every sprint. This global/site ability to configure search is critical for us as we embed the search portlet in the header in the theme and the search portlet instance configuration is not an option available to us. Even if we did not embed the portlet in the header, I can see value in being able to configure search globally or on a per site basis. . It would be great if this could be built into Liferay. In the meantime - is there anything we can do to do a support customization for the configuration of the search portlet? Couple of specific configuration examples I can think of are - restrict search scope to a specific site for a a site, disable user to show up in search, add active announcements to show up in search results.

Thanks again.
Posted on 9/18/13 7:47 AM.
Thanks for nice information,

I just want to know, if we want to include two or more extension for DLFileEntry How it can be done.
I am trying using entryClassName:com.liferay.portlet.journal.model.JournalArticle (+entryClassName:com.liferay.portlet.documentlibrary.model.DLFileEntry +extension:pdf (+extension:doc))

But it only search for pdf documents.

My question is how can we include multiple extension of file

Thanks
Posted on 1/23/14 5:38 PM.
For anyone interested in this functionality (just the core, not the configurable bit) but unclear on how to implement it- you need to create a new hook in eclipse and then:

1. Add this code from to main_search .jspf somewhere above the search results table (html/portlet/search)

Query stringQuery = StringQueryFactoryUtil.create("entryClassName:com.liferay.portlet.journal.model.­JournalArticle (+entryClassName:com.liferay.portlet.documentlibrary.model.DLFileEntry +extension:pdf)");

BooleanClause clause = BooleanClauseFactoryUtil.create(searchContext, stringQuery, BooleanClauseOccur.MUST.getName());

searchContext.setBooleanClauses(new BooleanClause[] {clause});


2. Declare the dependencies in init.jsp. i.e. add them to all the other dependencies at the top (html/portlet/search)

page import="com.liferay.portal.kernel.search.SearchContext" %><%@
page import="com.liferay.portal.kernel.search.BooleanClause" %><%@
page import="com.liferay.portal.kernel.search.BooleanClauseFactoryUtil" %><%@
page import="com.liferay.portal.kernel.search.BooleanClauseOccur" %><%@
page import="com.liferay.portal.kernel.search.StringQueryFactoryUtil" %><%@
page import="com.liferay.portal.kernel.search.Query" %><%@
Posted on 2/12/14 1:34 PM.
I'm glad to find something written about this API. Thanks! One follow up question:
SearchContext has methods like setAssetTagNames and setAssetCategoryIds . How do the tags and categories specified there interact with the queries set with setBooleanClauses? ANDed? ORed? Overridden?
Posted on 8/5/14 4:23 PM.
Hello,
Could you please complete the above code example?
I would like to know how the searchContext is used to do the actual search.
Regards,

Query stringQuery = StringQueryFactoryUtil.create("entryClassName:com.liferay.portlet.journal.model.­JournalArticle (+entryClassName:com.liferay.portlet.documentlibrary.model.DLFileEntry +extension:pdf)");

BooleanClause clause = BooleanClauseFactoryUtil.create(searchContext, stringQuery, BooleanClauseOccur.SHOULD.toString());

searchContext.setBooleanClauses(new BooleanClause[] {clause});
Posted on 8/18/14 2:32 AM.