Wednesday, 24 May 2017

AEM Search Suggestions

AEM provides out of the box Suggester API to populate suggestions for a particular keyword. com.day.cq.search.suggest.Suggester API provides methods to fetch suggestions and it provides a spell check feature as well.

An example would be:

import com.day.cq.search.suggest.Suggester;
import com.soh.services.SearchSuggestion;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.commons.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Session;

/**
 * SOH Search Suggester
 */
@Component
@Service(SearchSuggestion.class)
public class SearchSuggestionImpl implements SearchSuggestion {
    /**
     * The Suggester Index
     */
    private static final String INDEX = "oak-cq:Page";

    /**
     * CQ Suggester Reference
     */
    @Reference
    private Suggester suggester;

    /**
     * 
     * @param keyword
     *            to search for
     * @return JSONArray containing search suggestions
     */
    @Override
    public JSONArray getSuggestions(String keyword, int noOfItems, ResourceResolver resourceResolver) {
        final boolean spellCheck = true;
        final JSONArray suggestions = new JSONArray();
        final Session session = resourceResolver.adaptTo(Session.class);
        int counter = 0;
        for (final String suggestion : suggester.getSuggestions(session, INDEX, keyword, spellCheck)) {
            counter++;
            suggestions.put(suggestion);
            if (counter == noOfItems) {
                break;
            }
        }
        if (suggestions.length() < 1) {
            final String spellSuggestion = suggester.spellCheck(session, keyword);
            for (final String suggestion : suggester.getSuggestions(session, INDEX, spellSuggestion, spellCheck)) {
                counter++;
                suggestions.put(suggestion);
                if (counter == noOfItems) {
                    break;
                }
            }
        }

        return suggestions;
    }
}

Further, we can configure our index to specify which JCR properties do we want to include while populating suggestions. useInSuggest="{Boolean}true" is the property that we have to specify. Here is a sample index which includes jcr:title property in the suggestions:


<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:rep="internal"
    jcr:mixinTypes="[rep:AccessControllable]"
    jcr:primaryType="nt:unstructured">
    <customLuceneIndex
        jcr:primaryType="oak:QueryIndexDefinition"
        async="async"
        evaluatePathRestrictions="{Boolean}true"
        includedPaths="[/content/myproject]"
        reindex="{Boolean}false"
        type="lucene">
        <analyzers jcr:primaryType="nt:unstructured">
            <default
                jcr:primaryType="nt:unstructured"
                class="org.apache.lucene.analysis.standard.StandardAnalyzer">
                <stopwords/>
            </default>
        </analyzers>
        <indexRules jcr:primaryType="nt:unstructured">
            <cq:Page jcr:primaryType="nt:unstructured">
                <properties jcr:primaryType="nt:unstructured">
                    <jcrTitle
                        jcr:primaryType="nt:unstructured"
                        name="jcr:content/jcr:title"
                        nodeScopeIndex="{Boolean}true"
                        ordered="{Boolean}false"
                        propertyIndex="{Boolean}true"
                        type="String"
                        useInSuggest="{Boolean}true"/>
                </properties>
            </cq:Page>
        </indexRules>
    </customLuceneIndex>
</jcr:root>



Hope this helps!

1 comment: