All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.granite.omnisearch.commons.AbstractOmniSearchHandler Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*************************************************************************
 *
 * 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