April 1, 2020
Estimated Post Reading Time ~

How to create custom query predicate in CQ

Use Case: Your search has some custom requirement that can not be covered by existing predicate

Prerequisite:
http://www.pro-vision.de/content/medialib/pro-vision/production/adaptto/2011/2011_querybuilder-pdf/_jcr_content/renditions/rendition.file/2011_querybuilder.pdf
http://dev.day.com/docs/en/cq/current/dam/customizing_and_extendingcq5dam/query_builder.html
http://dev.day.com/docs/en/cq/current/javadoc/com/day/cq/search/QueryBuilder.html
http://dev.day.com/docs/en/cq/current/javadoc/com/day/cq/search/eval/PredicateEvaluator.html

Example:
Suppose you want to create a custom predicate to copare and sort case sensitive property.
Your predicate expression will look like

-----Signature -----

path=<Path under which search would be performed>
type=<node type>
customcase.property=<property name for which case sensitive search needs to be performed>
customcase.fulltext=<Search Text>
customcase.case=upper/lower/UPPER/LOWER/no_case/NO_CASE

orderby=customcase

You can test these example after deploying code HOST:PORT/libs/cq/search/content/querydebug.html

---- Example 1 (Find all node with subtitle as "MAIL" and do upper case compare) -----

path=/content/geometrixx/en
type=cq:Page
customcase.property=jcr:content/subtitle
customcase.fulltext=MAIL
customcase.case=upper
orderby=customcase

---- Example 2 (find all node and sort by subtitle property) -----
path=/content/geometrixx/en

type=cq:Page
customcase.property=jcr:content/subtitle
customcase.case=no_case
orderby=customcase


import java.util.Comparator;
import javax.jcr.query.Row;
import org.apache.felix.scr.annotations.Component;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.day.cq.search.Predicate;
import com.day.cq.search.eval.AbstractPredicateEvaluator;
import com.day.cq.search.eval.EvaluationContext;
/**
* Custom class to support case sensitive compare
* path=
* type=
* customcase.property=
* {optional} customcase.fulltext=
* customcase.case=upper/lower/UPPER/LOWER/no_case/NO_CASE
* orderby=customcase
* @author yogeshupadhyay
*
*/
@Component(metatype =false, factory="com.day.cq.search.eval.PredicateEvaluator/customcase")
public class CustomCasePredicate extends AbstractPredicateEvaluator {
private static final Logger logger = LoggerFactory.getLogger(CustomCasePredicate.class);
public static final String PROPERTY = "property";
public static final String FULL_TEXT = "fulltext";
public static final String CASE = "case";
public static final String LOWER_CASE = "lower";
public static final String UPPER_CASE = "upper";
public static final String NO_CASE = "no_case";
@Override
public boolean includes(Predicate predicate, Row row,
EvaluationContext context) {
if(predicate.hasNonEmptyValue(PROPERTY)){
return true;
}
return super.includes(predicate, row, context);
}
/**
* Create custom Xpath expression based on property
*/
@Override
public String getXPathExpression(Predicate predicate,
EvaluationContext context) {
if(!predicate.hasNonEmptyValue(PROPERTY)){
return null;
}
if(predicate.hasNonEmptyValue(PROPERTY) && null==predicate.get(FULL_TEXT)){
return super.getXPathExpression(predicate, context);
}
if(predicate.get(CASE).equalsIgnoreCase(LOWER_CASE)){
return "fn:lower-case(@"+predicate.get(PROPERTY)+")='"+predicate.get(FULL_TEXT)+"'";
}
if(predicate.get(CASE).equalsIgnoreCase(UPPER_CASE)){
return "fn:upper-case(@"+predicate.get(PROPERTY)+")='"+predicate.get(FULL_TEXT)+"'";
}
if(predicate.get(CASE).equalsIgnoreCase(NO_CASE)){
return "fn:lower-case(@"+predicate.get(PROPERTY)+")='"+predicate.get(FULL_TEXT).toLowerCase()+"'";
}
// TODO Auto-generated method stub
return super.getXPathExpression(predicate, context);
}

/**
* Some workaround is done to handle multi value property
* Multivalue is sorted based on first value in Array
*/
@Override
public Comparator<Row> getOrderByComparator(final Predicate predicate, final EvaluationContext context) {
return new Comparator<Row>() {
public int compare(Row r1, Row r2) {
String[] property1;
String[] property2;
try {
if(null!=r1&&null!=r2&&null!=predicate.get(PROPERTY)){
ValueMap valueMap1 = context.getResource(r1).adaptTo(ValueMap.class);
ValueMap valueMap2 = context.getResource(r2).adaptTo(ValueMap.class);
property1 = valueMap1.get(predicate.get(PROPERTY),String[].class);
property2 =valueMap2.get(predicate.get(PROPERTY),String[].class);
boolean isCompare = null!=property1 && null!=property2;
if(isCompare) logger.debug("value "+ property1[0] + " "+ property2[0]);
if(isCompare && predicate.get(CASE).equalsIgnoreCase(LOWER_CASE)){
return property1[0].toLowerCase().compareTo(property2[0].toLowerCase());
}
if(isCompare && predicate.get(CASE).equalsIgnoreCase(UPPER_CASE)){
return property1[0].toUpperCase().compareTo(property2[0].toUpperCase());
}else{
return isCompare ? property1[0].compareToIgnoreCase(property2[0]):1;
}
}
} catch (Exception e) {
logger.error(e.getMessage());
e.printStackTrace();
}
return 1;
}

};
}

}


One more example http://labs.sixdimensions.com/blog/joe-gergis/2013-07-10/custom-predicateevaluators-or-how-i-learned-stop-worrying-and-love

And one more

https://github.com/Adobe-Marketing-Cloud/aem-docs-custom-predicate-evaluator/blob/master/src/main/java/com/adobe/aem/docs/search/ReplicationPredicateEvaluator.java


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.