com.adobe.granite.omnisearch.commons.AbstractOmniSearchHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2015 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.granite.omnisearch.commons;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
import javax.jcr.observation.EventListener;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.vault.util.JcrConstants;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.granite.omnisearch.api.suggestion.PredicateSuggestion;
import com.adobe.granite.omnisearch.spi.core.OmniSearchHandler;
import com.day.cq.i18n.I18n;
/**
* AbstractOmniSearchHandler
is an abstract class
* which other can extend to provide implemenation of OmniSearchHandler
* If any Module implements OmniSearchHandler
using AbstractOmniSearchHandler
,
* It needs to create contentNode under path METADATA_PATH. And provide all the property details in that Node.
* Properties:
*
* - IS_SUGGESTABLE_PROPERTY
- Property that decide if Predicate should be used in predicate suggestions
* - METADAT_PATH
- PATH where metadata of all the modules will be stored
* - NODE_TYPE_PROPERTY
- Resource type of Module (eg. dam:Asset)
* - OPTION_PATH_PROPERTY
- optionPath property of a Predicate
* - PREDICATE_PATH_PROPERTY
- property that stores Predicate Path in METADATA Node
* - DEFAULT_SEARCH_PATH_PROPERTY
- property that stores Default Search Path in METADATA Node
* - PREDICATE_TYPE_PROPERTY
- property that identifies type of Predicate
*
*/
public abstract class AbstractOmniSearchHandler implements OmniSearchHandler, EventListener {
protected final String IS_SUGGESTABLE_PROPERTY = "isSuggestable";
/**
* This service user has been created in GRANITE
* It has default read access to "/libs/granite/omnisearch/content/metadata" and "/libs/settings"
* If Modules are using this service user, they need to bind it their bundle and
* if their Predicate
or Option for Predicate
are not in above
* mentioned Path, Module owner need to provide access to necessary paths as done for Assets and Sits
*/
protected final String OMNI_SEARCH_SERVICE_USER = "omnisearch-service";
private final String PREDICATE_GROUP_CONSTANT = "group";
private final String PREDICATE_PROPERTY_CONSTANT = "property";
private final String PREDICATE_VALUE_CONSTANT = "value";
private final String PREDICATE_OR_CONSTANT = "p.or";
public final String METADATA_PATH = "/libs/granite/omnisearch/content/metadata";
private final String NODE_TYPE_PROPERTY ="nodeType";
private final String OPTION_PATH_PROPERTY = "optionPaths";
private final String PREDICATE_PATH_PROPERTY = "predicatePath";
private final String DEFAULT_SEARCH_PATH_PROPERTY = "defaultSearchPath";
private final String PREDICATE_TYPE_PROPERTY = "text";
private final String INCLUDE_IN_SUGGESTIONS = "includeInSuggestions";
private final String SUGGESTION_FROM_SEARCH_PATH_PROPERTY = "suggestionFromSearchPath";
private final String PREDICATE_PROPERTY_NAME = "name";
private final String LIST_ORDER = "listOrder";
private final String LOCATION_PREDICATE = "location";
private final String SEARCH_RAIL_PATH_PROPERTY = "searchRailPath";
private ModuleInformation moduleInformation = null;
private final Logger log;
public AbstractOmniSearchHandler() {
log = LoggerFactory.getLogger(this.getClass());
}
/**
* This function returns Query
that provides suggestions
* based on parameters provide in the request. It will look for "fulltext"
* paramter in request and "fulltext" parameter will treated as search term.
* Based on this search term suggestion query will be created.
* @param resolver ResourceResolver instance
* @param searchTerm text term for which suggestions are require
* @return Query
that returns suggestions on execution
*/
public Query getSuggestionQuery(ResourceResolver resolver, String searchTerm) {
boolean includeInSuggestions = moduleInformation.includeInSuggestions();
if(includeInSuggestions == false) {
return null;
}
try {
//nodetype and defaultSearchPath will have to concatenated, as these varriable can not be given using bind variable
// throws ParseException, if I use bindVariable for nodetype and defaultSearchPath as well
boolean suggestionFromSearchPath = moduleInformation.suggestionFromSearchPath();
final String descendantNodeCondition = (suggestionFromSearchPath) ? " AND ISDESCENDANTNODE([" + getDefaultSearchPath() + "])" : "";
final String queryStr = "SELECT [rep:suggest()] FROM [" + getResourceType() + "] as s WHERE SUGGEST($term)" + descendantNodeCondition;
final Query query = createQuery(resolver, searchTerm, queryStr);
log.debug("Suggestion query {}", query.toString());
return query;
} catch (RepositoryException e) {
log.error("Error while creating Suggestion query", e);
}
return null;
}
/**This function provide List of Predicates that matched to current
* request parameters. It will match the value of PredicateSuggestion
* to the search term. Currently predicate suggestion works only if
* length of search term is more than MIN_SUGGESTION_REQUIRE_SIZE
* @param resolver ResourceResolver instance
* @param i18n I18n instance
*@param searchTerm text term for which suggestions are require @return List
of PredicateSuggestion
*/
public List getPredicateSuggestions(ResourceResolver resolver, I18n i18n, String searchTerm) {
List matchedPredicateList = new ArrayList();
List predicateSuggestionList = getPredicateSuggestions(resolver, i18n);
if ( !predicateSuggestionList.isEmpty()) {
for( PredicateSuggestion predicateSuggestion : predicateSuggestionList) {
if (predicateSuggestion.getOptionTitle().toLowerCase().contains(searchTerm.toLowerCase())) {
if(predicateSuggestion.getQueryParameters() == null) {
predicateSuggestion.setQueryParameters(getQueryParameters(predicateSuggestion, resolver));
}
if(resolver.getResource(predicateSuggestion.getTypePath()) != null && resolver.getResource(predicateSuggestion.getOptionPath()) != null){
matchedPredicateList.add(predicateSuggestion);
}
}
}
}
return matchedPredicateList;
}
/**
* This function returns Query
that provides spell check suggestions
* based on parameters provide in the request. It will look for "fulltext"
* paramter in request and "fulltext" parameter will treated as search term.
* Based on this search term spell check query will be created.
* @param resolver ResourceResolver instance
* @param searchTerm text term for which suggestions are require
* @return Query
that returns spell check suggestion on execution.
*/
public Query getSpellCheckQuery(ResourceResolver resolver, String searchTerm) {
boolean includeInSuggestions = moduleInformation.includeInSuggestions();
if(includeInSuggestions == false) {
return null;
}
try {
final String queryStr = "SELECT [rep:spellcheck()] FROM [" + getResourceType() + "] as s WHERE [jcr:path] = '/' AND SPELLCHECK($term)";
final Query query = createQuery(resolver, searchTerm, queryStr);
log.debug("Spellcheck query {}", query.toString());
return query;
} catch (RepositoryException e) {
log.error("Error while creating Spellcheck query", e);
}
return null;
}
private Query createQuery(ResourceResolver resolver, String searchTerm, String queryStr) throws RepositoryException {
final Session session = resolver.adaptTo(Session.class);
final QueryManager queryManager = session.getWorkspace().getQueryManager();
final Query query = queryManager.createQuery(queryStr,Query.JCR_SQL2);
final ValueFactory vf = session.getValueFactory();
query.bindValue("term", vf.createValue(searchTerm));
return query;
}
/**
* returns Path for config content node for the Module.
* It is located under Node at METADATA_DATA
path.
* Node at this path contains information regarding Search Module
* identified by location i.e. getID()
*/
protected String getModuleConfigNodePath() {
return METADATA_PATH + "/" + getID();
}
public PredicateSuggestion getLocationSuggestion(ResourceResolver resolver, I18n i18n, String searchTerm) {
if (i18n.get(getName()).toLowerCase().contains(searchTerm.toLowerCase())) {
Map queryParameters = new HashMap();
queryParameters.put(LOCATION_PREDICATE, getID());
PredicateSuggestion locationPredicateSuggestion = new PredicateSuggestion(LOCATION_PREDICATE, getName());
locationPredicateSuggestion.setQueryParameters(queryParameters);
return locationPredicateSuggestion;
}
return null;
}
/**This function provide a Name of OmniSearchHandler
* This will display as Module Name at UI.
* @return Name of OmniSearchHandler
*/
protected String getName() {
return moduleInformation.getTitle();
}
/**
* This function returns the resourceType of the OmniSearchHandler
implementations
* @return resource type
*/
protected String getResourceType() {
return moduleInformation.getResourceType();
}
/**
* This function returns the resource that contains predicate list
* for the module.
*
* If a module need to support /conf usage, then this method should be overridden
*
* Default Implementation simply return resolver.getResource(getPredicatePath())
* @param resolver resolver
* @return resource
*/
protected Resource getPredicateRootResource(ResourceResolver resolver) {
return resolver.getResource(moduleInformation.getPredicatePath());
}
/**
* This function returns the location of predicate of the Module
* that implements OmniSearchHandler
* @return predicate path
*/
protected String getPredicatePath() {
return moduleInformation.getPredicatePath();
}
/**
* This function returns the default Search Path of the Module
* that implements OmniSearchHandler
* @return predicate path
*/
protected String getDefaultSearchPath() {
return moduleInformation.getDefaultSearchPath();
}
/**
* This function updates the list of PredicateSuggestion
that are available for the Module.
* This function reads the predicates from _predicatePath. and check if Predicate has IS_SUGGESTABLE_PROPERTY as true.
* In that case , it looks for options for this predicates and update list with type from PREDICATE_TYPE_PROPERTY property of
* predicate and title from jct:title property of option.
* @param resolver ResourceResolver
*/
private List getPredicateSuggestions(ResourceResolver resolver, I18n i18n) {
List predicateSuggestionList = new ArrayList();
String predicatePath = moduleInformation.getPredicatePath();
if (StringUtils.isNotEmpty(predicatePath)) {
Resource predicateRoot = getPredicateRootResource(resolver);
if (predicateRoot == null) {
log.warn("Non-Existent Predicate Path " + predicatePath);
return Collections.emptyList();
}
extractPredicateSuggestions(resolver, predicateSuggestionList, predicateRoot, i18n);
} else {
log.debug("Invalid Predicate Path {} skipping predicate suggestion loading ", predicatePath);
}
return predicateSuggestionList;
}
private void extractPredicateSuggestions(ResourceResolver resolver, List predicateSuggestionList, Resource predicateRoot, I18n i18n) {
for (Resource predicateResource : predicateRoot.getChildren()) {
ValueMap vm = predicateResource.adaptTo(ValueMap.class);
boolean isSuggestable = vm.get(IS_SUGGESTABLE_PROPERTY, false);
String optionsPath = vm.get(OPTION_PATH_PROPERTY, String.class);
String predicateType = i18n.getVar(vm.get(PREDICATE_TYPE_PROPERTY, String.class));
if (isSuggestable && optionsPath != null && predicateType != null) {
Resource optionsRes = resolver.getResource(optionsPath);
if (optionsRes != null) {
for (Resource resource : optionsRes.getChildren()) {
ValueMap optionProps = resource.adaptTo(ValueMap.class);
String predicateTitle = i18n.getVar(optionProps.get(JcrConstants.JCR_TITLE, String.class));
if (predicateTitle != null) {
PredicateSuggestion currPredicateSuggestion = new PredicateSuggestion(predicateType, predicateTitle, predicateResource.getPath(), resource.getPath());
currPredicateSuggestion.setQueryParameters(getQueryParameters(currPredicateSuggestion, resolver));
predicateSuggestionList.add(currPredicateSuggestion);
}
}
}
}
extractPredicateSuggestions(resolver, predicateSuggestionList, predicateResource, i18n);
}
}
/**This function add query parameters to the PredicateSuggestions
which
* UI will add in the search query or URL.
* Currently this method create and add, query parameters to the Predicates
of metatype "option" or
* "listoption".
* if any module has Predicates
into their PredicateSuggestion
list which are not of type "option" or "listoption",
* module owner should override this method in their OmniSearchHandler
implementation to meet their requirement.
* @param predicateSuggestion PredicateSuggestion
for which query parameters needed be add
* @param resolver ResourceResolver
instance
* @return a map
*/
protected Map getQueryParameters(PredicateSuggestion predicateSuggestion, ResourceResolver resolver) {
Map queryParameters = new HashMap();
Resource predicateResource = resolver.getResource(predicateSuggestion.getTypePath());
ValueMap vm = predicateResource.adaptTo(ValueMap.class);
String listOrder = vm.get(LIST_ORDER, String.class);
String propertyType = vm.get(PREDICATE_PROPERTY_NAME, String.class);
Resource optionResource = resolver.getResource(predicateSuggestion.getOptionPath());
List valueList = getOptionValuesList(optionResource);
if(valueList.isEmpty()) {
return null;
}
if(valueList.size() > 1) {
//groupPredicate
String predicateGroupName = PREDICATE_GROUP_CONSTANT;
if (listOrder != null) {
predicateGroupName = listOrder + "_" + predicateGroupName;
}
String predicateProperty = predicateGroupName + "." + PREDICATE_PROPERTY_CONSTANT;
queryParameters.put(predicateProperty, propertyType);
int valueCount = 0;
for(String value : valueList) {
valueCount++;
queryParameters.put(predicateProperty + "." + valueCount + "_" + PREDICATE_VALUE_CONSTANT, value);
}
queryParameters.put(predicateGroupName + "." + PREDICATE_OR_CONSTANT,"true");
} else {
String predicateProperty = PREDICATE_PROPERTY_CONSTANT;
if(listOrder != null) {
predicateProperty = listOrder + "_" + predicateProperty;
}
queryParameters.put(predicateProperty, propertyType);
queryParameters.put(predicateProperty + "." + PREDICATE_VALUE_CONSTANT, valueList.get(0));
}
return queryParameters;
}
/**
* Return the moduleConfiguration resource for this handler.
*
* @param resolver the resourceResolver to use
* @return the resource where the configuration is stored.
*
*/
public Resource getModuleConfig(ResourceResolver resolver) {
Resource moduleConfigResource = resolver.getResource(getModuleConfigNodePath());
if (moduleConfigResource == null) {
log.debug("no module config resource located at path {}", getModuleConfigNodePath());
}
return moduleConfigResource;
}
private List getOptionValuesList(Resource optionResource) {
List valueList = new ArrayList();
if(optionResource != null) {
ValueMap optionResValueMap = optionResource.getValueMap();
String optionvalue = optionResValueMap.get("value",String.class);
if(!"".equals(optionvalue) && optionvalue != null) {
valueList.add(optionvalue);
}
}
Iterator childRes = optionResource.getChildren().iterator();
while (childRes.hasNext()) {
Resource child = childRes.next();
valueList.addAll(getOptionValuesList(child));
}
return valueList;
}
/**
* This function intialize the ResourceResolver
instace.
* It usually called on activation of OmniSearchHandler
or on event from
* implementation of OmniSearchHandler
* @param resolver ResourceResolver
instance, AbstractOmniSearchHandler
expects that
* this ResourceResolver
will not be closed before deactivation.
*
* @deprecated Use initialize
instead.
*/
public void init(ResourceResolver resolver) {
initialize(resolver);
}
/**
* initialize the AbstractOmniSearchHandler.
* @param resolver a ResourceResolver to read the relevant information from the repository. After
* calling this method the resolver can be closed, it's not expected to keep it open.
*/
public void initialize(ResourceResolver resolver) {
moduleInformation = new ModuleInformation(resolver);
}
/**
* This function clear the predicateSuggestionList and remove all the eventListeners.
* This is usually called on deactivation of OmniSearchHandler
* @param resolver ResourceResolver
instance
*
* @deprecated Removed without replacement (it always was an empty method)
*/
public void destroy(ResourceResolver resolver) {
}
/**
* A simple pojo which holds all module information
*
*/
private class ModuleInformation {
String title;
String resourceType;
String predicatePath;
String defaultSearchPath;
boolean includeInSuggestions;
boolean suggestionFromSearchPath;
/**
* Read all information from the provided resource; it does not use the
* resourceResolver afterwards
*
* @param r
*/
public ModuleInformation(ResourceResolver resolver) {
Resource moduleConfigResource = getModuleConfig(resolver);
if (moduleConfigResource != null) {
ValueMap vm = moduleConfigResource.adaptTo(ValueMap.class);
title = vm.get(JcrConstants.JCR_TITLE, String.class);
resourceType = vm.get(NODE_TYPE_PROPERTY, String.class);
String searchRailPath = vm.get(SEARCH_RAIL_PATH_PROPERTY, String.class);
if (!StringUtils.isEmpty(searchRailPath)) {
Resource searchPanelResource = resolver.getResource(searchRailPath);
if (searchPanelResource != null) {
ValueMap searchProperties = searchPanelResource.adaptTo(ValueMap.class);
predicatePath = searchProperties.get(PREDICATE_PATH_PROPERTY, "");
}
} else {
predicatePath = vm.get(PREDICATE_PATH_PROPERTY, String.class);
}
defaultSearchPath = vm.get(DEFAULT_SEARCH_PATH_PROPERTY, String.class);
includeInSuggestions = vm.get(INCLUDE_IN_SUGGESTIONS, false);
suggestionFromSearchPath = vm.get(SUGGESTION_FROM_SEARCH_PATH_PROPERTY, true);
}
}
public String getTitle() {
return title;
}
public String getResourceType() {
return resourceType;
}
public String getPredicatePath() {
return predicatePath;
}
public String getDefaultSearchPath() {
return defaultSearchPath;
}
public boolean includeInSuggestions() {
return includeInSuggestions;
}
public boolean suggestionFromSearchPath() {
return suggestionFromSearchPath;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy