Sunday, June 28, 2020

How to implement autocompletion and search suggestion in AEM through Lucene | Predictive Search in AEM | AEM Search Suggestions

How to implement autocompletion and search suggestion in AEM through Lucene | Predictive Search in AEM | AEM Search Suggestions


This tutorial explain the approach to implement autocompletion and search suggestion in AEM through Lucene.

When you start typing something in search form most of the applications helps you by suggesting the data matching to your search term.

aem-autocompletion-search-suggestion

The purpose of autocomplete is to resolve a partial query , i.e., to search within a controlled vocabulary for items matching a given character string.

Starting from AEM 6.1 the feature of suggestion is available through the suggest module of Lucene. Prior to AEM 6.1, all the possible combination of the words needs to be indexed to support the autocompletion.

The Lucene Suggest module provides a dedicated and optimized data structure allows the engine to give autocompletion and suggestion feature without indexing all the possible combination of a word. 

There is a specific analyzer (AnalyzingInfixSuggester) used that loads the completion values from the indexed data and then build the optimized structure in memory for a fast lookup. 

In order to implements the autosuggestion, feature you need to define an index of type Lucene and for each property X of nodes that you are indexing you can add a specific property useInSuggest to tell to the engine to use X for suggesting query to the user.

Refer the following URL for details on enabling custom index - https://www.albinsblog.com/2020/04/oak-lucene-index-improve-query-in-aem-configure-lucene-index.html  

I have already enabled a custom Lucene index(testindex) for the property "id", add a property "useInSuggest" to tell the engine to use id for suggesting query to the user.

aem-autocompletion-search-suggestion


An additional property suggestUpdateFrequencyMinutes define the frequency of updating the indexed suggestions - useful to mitigate performance issues that can arise if indexed properties are frequently updated by the users of your application. The default value is 10 minutes but the values can be modified as required.

To enable the property "suggestUpdateFrequencyMinutes ", create a node with name "suggest" of type "nt:unstructured" under "testindex" and update the value as required

aem-autocompletion-search-suggestion


 In order to use Lucene index to perform search suggestions, the index definition node (the one of type oak:QueryIndexDefinition) needs to have the compatVersion set to 2. 

aem-autocompletion-search-suggestion


Let us now execute the query to find the suggestions - either one of the below query can be used.

The testindex was defined for the content path "/content/sampledata" so the query will be executed based on the "testindex" but the index name is explicitly defined in the first query.

aem-autocompletion-search-suggestion


SELECT [rep:suggest()] FROM [nt:unstructured] WHERE SUGGEST('te') OPTION(INDEX NAME [testindex]) /* oak-internal */ 

SELECT [rep:suggest()] FROM [nt:unstructured] WHERE SUGGEST('te') AND ISDESCENDANTNODE('/content/sampledata')

The above query uses path restriction to filter the data, it requires evaluatePathRestrictions property should enabled as true on index definition.

aem-autocompletion-search-suggestion


The Query tool shows the total number of unique suggestions matching with the search data but it wont displays the matching node details

aem-autocompletion-search-suggestion


The below Servlet can be used to fetch the suggestion data through QueryManager API
import java.io.IOException;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;
import javax.servlet.Servlet;
import javax.servlet.ServletException;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.json.JSONArray;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(immediate = true, service = Servlet.class, property = {
		Constants.SERVICE_DESCRIPTION + "=Custom Root Mapping", "sling.servlet.methods=" + HttpConstants.METHOD_GET,
		"sling.servlet.paths=" + "/bin/getSuggestions", "service.ranking=" + 100001 })
public class SuggestionData extends SlingSafeMethodsServlet {

	/**
	* 
	*/
	private static final long serialVersionUID = 1L;

	protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp)
			throws ServletException, IOException {

		Logger logger = LoggerFactory.getLogger(this.getClass());
		logger.error("inside custom servlet");

		final Session session = req.getResourceResolver().adaptTo(Session.class);

		final JSONArray suggestions = new JSONArray();

		String queryString = "SELECT [rep:suggest()]  FROM [nt:unstructured] WHERE "
							 +"SUGGEST('te') OPTION(INDEX NAME [testindex]) /* oak-internal */ ";

		try {
			QueryManager queryManager = session.getWorkspace().getQueryManager();
			Query query = queryManager.createQuery(queryString, Query.JCR_SQL2);
			QueryResult result = query.execute();
			RowIterator rows = result.getRows();

			while (rows.hasNext()) {
				suggestions.put(((Row) rows.next()).getValue("rep:suggest()").getString());
			}

		} catch (InvalidQueryException e) { // TODO Auto-generated catch block
			e.printStackTrace();
		} catch (RepositoryException e) { // TODO Auto-generated
			e.printStackTrace();
		} finally {
			session.logout();

		}

		resp.setContentType("application/json");
		resp.getWriter().write(suggestions.toString());

	}

}

aem-autocompletion-search-suggestion


This suggestion data cab be used to display  the search autocompletion/suggest data to the website users.





No comments:

Post a Comment