Goal
Create a predicate evaluator for ordering Query Builder - /bin/querybuilder.json results based on node properties, Case Ignored...
Thanks to this blog post for providing insight on predicate evaluators and documentation on docs.adobe.com
So with the following query
http://localhost:4502/bin/querybuilder.json?p.limit=20&p.offset=0&eaem-ignore-case.property=jcr:content/metadata/uaDIO:reportName&orderby=eaem-ignore-case&fulltext=*.indd&p.hits=full&p.nodedepth=2&path=/content/dam/Product/Assortments&type=dam:Asset
prettified....
p.limit: 20
p.offset: 0
eaem-ignore-case.property: jcr:content/metadata/uaDIO:reportName
orderby: eaem-ignore-case
fulltext: *.indd
p.hits: full
p.nodedepth: 2
path: /content/dam/Product/Assortments
type: dam:Asset
eaem-ignore-case predicate evaluator getOrderByProperties() returns the property jcr:content/metadata/uaDIO:reportName adding necessary xpath function fn:upper-case and the final xpath query built would be....
/jcr:root/content/dam/Product/Assortments//element(*, dam:Asset)[jcr:contains(., '*.indd')] order by fn:upper-case(jcr:content/metadata/@uaDIO:reportName)
Solution
1) Create a OSGI bundle with case insensitive predicate evaluator class, add the following code
package apps.experienceaem.pe; import com.day.cq.search.Predicate; import com.day.cq.search.eval.AbstractPredicateEvaluator; import com.day.cq.search.eval.EvaluationContext; import org.apache.felix.scr.annotations.Component; import org.apache.sling.api.resource.ValueMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.query.Row; import java.util.*; @Component(metatype = false, factory = "com.day.cq.search.eval.PredicateEvaluator/eaem-ignore-case") public class CaseInsensitiveOrderByPredicate extends AbstractPredicateEvaluator { private static final Logger logger = LoggerFactory.getLogger(CaseInsensitiveOrderByPredicate.class); public static final String PROPERTY = "property"; public String[] getOrderByProperties(Predicate p, EvaluationContext context) { Map<String, String> paramMap = p.getParameters(); List<String> orderProps = new ArrayList<String>(); for(String param : paramMap.values()){ orderProps.add("fn:upper-case(" + param + ")"); } return orderProps.toArray(new String[0]); } /** * can be used for further ordering, or scenarios where getOrderByProperties() isn't enough * * @param predicate * @param context * @return */ /*public Comparator<Row> getOrderByComparator(final Predicate predicate, final EvaluationContext context) { return new Comparator<Row>() { public int compare(Row r1, Row r2) { int ret = 1; if ((r1 == null) || (r2 == null) || (predicate.get(PROPERTY) == null)) { return ret; } try { ValueMap valueMap1 = context.getResource(r1).adaptTo(ValueMap.class); ValueMap valueMap2 = context.getResource(r2).adaptTo(ValueMap.class); String property1 = valueMap1.get(predicate.get(PROPERTY), ""); String property2 = valueMap2.get(predicate.get(PROPERTY), ""); ret = property1.compareToIgnoreCase(property2); } catch (Exception e) { logger.error(e.getMessage()); } return ret; } }; }*/ }
2) Check registered evaluators - http://localhost:4502/system/console/services?filter=%28component.factory%3Dcom.day.cq.search.eval.PredicateEvaluator%2F*%29
I was really hoping this would work. I have the same issue and do not want to use the Comparator because its so slow.
ReplyDeleteUnfortunately, what you suggested does not work. At least it does not work on 6.1, maybe on 6.0sp2 it did.
You stated that the final XPATH would be
/jcr:root/content/dam/Product/Assortments//element(*, dam:Asset)[jcr:contains(., '*.indd')] order by fn:upper-case(jcr:content/metadata/@uaDIO:reportName)
When it is in fact
/jcr:root/content/dam/Product/Assortments//element(*, dam:Asset)[jcr:contains(., '*.indd')] order by fn:upper-case_x0028_jcr:content/metadata/@uaDIO:reportName_x0029_
The encoding of the ( and ) characters makes it not work.
Any thoughts on this issue?
kyle, i think it doesn't work for 61 until the issue CQ-40967 (internal tracking number) gets fixed.. contact day care
Delete