Blogs
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!

