AEM 6 SP2 - Query Builder Predicate Evaluator for ordering results Ignoring Case

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


2 comments:

  1. I was really hoping this would work. I have the same issue and do not want to use the Comparator because its so slow.

    Unfortunately, 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?


    ReplyDelete
    Replies
    1. kyle, i think it doesn't work for 61 until the issue CQ-40967 (internal tracking number) gets fixed.. contact day care

      Delete