Creating a Google Like Search Part II: Filter by Structure and Document Type

Creating a Google Like Search Part II: Filter by Structure and Document Type

(Previous part of the series can be found here)

In the second part of this blog series I’ll be adding new filtering capabilities to the portlet created in the first part. Filters added are filter by web content structure and filter by file document type and extension. That way, if you have for example defined a web content structure “News” or a document type, let’s say “Contract”, you can search only for those. Filtering by document extension is also there so you can search only for “Contracts” of type PDF for example.

You may notice on the project Github page that the codebase looks a little different now. I did some streamlining and refactored the code to make more use of OSGI declarative services. I also added there a search support for Wiki pages and made the selection of supported types configurable.

How it was done?

Starting here with the UI, I first added there in GSearchFilters.soy the Twitter Bootstrap dropdown components having the dropdown skeletons:

<div class="dropdown gsearch-dropdown filter wcfilter hide">
 <button 
      aria-expanded="true" 
      aria-haspopup="true" 
      class="btn btn-link dropdown-toggle" 
      data-toggle="dropdown" 
      id="{$portletNamespace}WebContentStructureFilter" 
      type="button">
						
      <span class="selection"></span>
      <span class="caret"></span>
   </button>

   <ul
      aria-labelledby="{$portletNamespace}WebContentStructureFilter" 
      class="dropdown-menu" 
      id="{$portletNamespace}WebContentStructureFilterOptions">
						
      <li class="selected">
 <a data-value="" href="#">{msg desc=""}any-web-content-structure{/msg}</a>
      </li>
						
      {call .options}
         {param options: $webContentStructureOptions /}
      {/call}
   </ul>
</div>		

There’s also a closure template in the same file for rendering the options items which are coming from the template context:

/**
 * Print dropdown options.
 *
 * @param options
 */
{template .options}
	{foreach $item in $options}
		{if $item.scope == 'all'}
			<li class="all">		
		{else}
			<li>
		{/if}
		
		{if $item.facet}
			<a data-facet="{$item.facet}" data-value="{$item.key}" href="#">
		{else}
			<a data-value="{$item.key}" href="#">
		{/if}

		{$item.name}

		{if $item.groupName}
			<span class="groupname">({$item.groupName})</span>
		{/if}				
		<span class="count"></span></a></li>
	{/foreach}
{/template}

In the Corresponding component class GSearchFilters.es.js I simply added creation of the dropdown option item click events, settings the selected item name and triggering the visibility of these, based on the asset and type selections. Parameter keys are added to the GSearchQuery.es.js class and new language keys to the Language.properties files.

On the backend side, in GSearchDisplayConfiguration, I added a configuration option for document type extensions. Well, it’s not nice configuration syntax I’m using there but for the sake of this exercise, it works. I generally tried to add there enough help texts for all the options.

Next in the ViewMVCRenderCommand class I’m adding there the options in the SOY template context:

template.put(GSearchWebKeys.WEB_CONTENT_STRUCTURE_OPTIONS,
_webContentStructureOptions.getOptions(renderRequest, _gSearchDisplayConfiguration));

In the same class, in setInitialParameters() I’m also adding the handling of these new parameters, if they are coming from the page calling url. For all the filter options there are dedicated services which are referenced like:

@Reference
protected WebContentStructureOptions _webContentStructureOptions;

The service has a declaring interface and at least one implementation. For example, for the web content structure options there’s the interface fi.soveltia.liferay.gsearch.web.search.menuoption.WebContentStructureOptions:

public interface WebContentStructureOptions {

	/**
	 * Get options.
	 * 
	 * @param portletRequest
	 * @param gSearchDisplayConfiguration
	 * @return options JSON array
	 * @throws Exception
	 */
	public JSONArray getOptions(
		PortletRequest portletRequest,
		GSearchDisplayConfiguration gSearchDisplayConfiguration)
		throws Exception;
}

..and the implementation, in this case  fi.soveltia.liferay.gsearch.web.search.internal.menuoption.WebContentStructureOptionsImpl. In the actual implementation I’ll collect the global group, all the users site groups and all public site groups and then using DDMStructureService, get the accessible document type options.

List structures = _ddmStructureService.getStructures(
	themeDisplay.getCompanyId(), groupIds, classNameId,
	WorkflowConstants.STATUS_APPROVED);

For the resulting JSON I’m tagging the options with “scope” all, if they are not from the current scope group. That way I can switch the visibility of available types by scope in the UI.

The last things to do is to pipe these new parameters from search request to the query filters.

For that I added the new parameters in fi.soveltia.liferay.gsearch.web.search.internal.queryparams.QueryParams pojo, handling of parameters in fi.soveltia.liferay.gsearch.web.search.internal.queryparams.QueryParamsBuilder.impl. Finally I add the new parameters to the query filters in fi.soveltia.liferay.gsearch.web.search.internal.query.filter.QueryFilterBuilderImpl:

/**
 * Add web content structure condition.
 * 
 * @throws ParseException
 */
 protected void buildWebContentStructureCondition()
	throws ParseException {

	String structureKey = _queryParams.getWebContentStructureKey();

	if (structureKey != null) {
	   _filter.addRequiredTerm("ddmStructureKey", structureKey);
	}
}	

That’s it for this time. The code is available at https://github.com/peerkar/liferay-gsearch

Blogs
[...] (Previous parts of this series can be found here (part 1), here (part 2) and here (part 3) [...] Read More
[...] Previous parts of this series can be found here (part 1), here (part 2), here (part 3) and here (part 4). In the final part of this blog series few more interesting features are added to the... [...] Read More