Implementing a Custom Predicate Evaluator for the Query Builder
- Applies to:
- Experience Manager 6.5
- Topics:
- Developing,Search,Query Builder
CREATED FOR:
- Developer
This section describes how to extend the Query Builder by implementing a custom predicate evaluator.
Overview
The Query Builder offers an easy way of querying the content repository. CQ ships with a set of predicate evaluators that helps you deal with your data.
However you might want to simplify your queries by implementing a custom predicate evaluator that hides some complexity and ensures better semantics.
A custom predicate could also perform other things not directly possible with XPath, for example:
- looking up some data from some service
- custom filtering based on calculation
CODE ON GITHUB
You can find the code of this page on GitHub.
- Open aem-search-custom-predicate-evaluator project on GitHub
- Download the project as a ZIP file
Predicate Evaluator in Detail
A predicate evaluator handles the evaluation of certain predicates, which are the defining constraints of a query.
It maps a higher-level search constraint (such as “width > 200”) to a specific JCR query that fits the actual content model (for example, metadata/@width > 200). Or it can manually filter nodes and check their constraints.
PredicateEvaluator
and the com.day.cq.search
package, see the Java™ documentation.Implementing a Custom Predicate Evaluator for Replication Metadata
As an example this section describes how to create a custom predicate evaluator that helps data based on the replication metadata:
-
cq:lastReplicated
that stores the date of the last replication action -
cq:lastReplicatedBy
that stores the id of the user who triggered the last replication action -
cq:lastReplicationAction
that stores the last replication action (for example, Activation, Deactivation)
Querying Replication Metadata with Default Predicate Evaluators
The following query fetches the list of nodes in /content
branch that have been activated by admin
since the beginning of the year.
path=/content
1_property=cq:lastReplicatedBy
1_property.value=admin
2_property=cq:lastReplicationAction
2_property.value=Activate
daterange.property=cq:lastReplicated
daterange.lowerBound=2013-01-01T00:00:00.000+01:00
daterange.lowerOperation=>=
This query is valid but hard to read and does not highlight the relationship between the three replication properties. Implementing a custom predicate evaluator reduces the complexity and improve the semantic of this query.
Objectives
The goal of the ReplicationPredicateEvaluator
is to support the above query using the following syntax.
path=/content
replic.by=admin
replic.since=2013-01-01T00:00:00.000+01:00
replic.action=Activate
Grouping replication metadata predicates with a custom predicate evaluator helps to create a meaningful query.
Updating Maven Dependencies
First, update the Maven dependencies of your project. The PredicateEvaluator
is part of the cq-search
artifact so it must be added to your Maven pom.xml file.
cq-search
dependency is set to provided
because cq-search
is provided by the OSGi
container.pom.xml
The following snippet shows the differences in unified diff format
@@ -120,6 +120,12 @@
<scope>provided</scope>
<dependency>
+ <groupid>com.day.cq</groupid>
+ <artifactid>cq-search</artifactid>
+ <version>5.6.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version></dependency>
aem-search-custom-predicate-evaluator - pom.xml
Writing The ReplicationPredicateEvaluator
The cq-search
project contains the AbstractPredicateEvaluator
abstract class. This can be extended with a few steps to implement your own custom predicate evaluator (PredicateEvaluator
).
Xpath
expression to filter data. Another option would be to implement the includes
method that selects data on a row basis. See the Java™ documentation for more information.-
Create a Java™ class which extends
com.day.cq.search.eval.AbstractPredicateEvaluator
-
Annotate your class with a
@Component
like the followingsrc/main/java/com/adobe/aem/docs/search/ReplicationPredicateEvaluator.java
The following snippet shows the differences in unified diff format
@@ -19,8 +19,11 @@
*/
package com.adobe.aem.docs.search;
+import org.apache.felix.scr.annotations.Component;
+
import com.day.cq.search.eval.AbstractPredicateEvaluator;
+@Component(metatype = false, factory = "com.day.cq.search.eval.PredicateEvaluator/repli")
public class ReplicationPredicateEvaluator extends AbstractPredicateEvaluator {
}
aem-search-custom-predicate-evaluator - src/main/java/com/adobe/aem/docs/search/ReplicationPredicateEvaluator.java
factory
must be a unique string starting with com.day.cq.search.eval.PredicateEvaluator/
and ending with the name of your custom PredicateEvaluator
.PredicateEvaluator
is the predicate name, which is used when building queries.-
Override:
public String getXPathExpression(Predicate predicate, EvaluationContext context)
In the override method, you build a
Xpath
expression based on thePredicate
given in argument.
Example of a Custom Predicate Evaluator for Replication Metadata
The complete implementation of this PredicateEvaluator
might be similar to the following class.
src/main/java/com/adobe/aem/docs/search/ReplicationPredicateEvaluator.java
/*
* #%L
* aem-docs-custom-predicate-evaluator
* %%
* Copyright (C) 2013 Adobe Research
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.adobe.aem.docs.search;
import org.apache.felix.scr.annotations.Component;
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;
@Component(metatype = false, factory = "com.day.cq.search.eval.PredicateEvaluator/repli")
public class ReplicationPredicateEvaluator extends AbstractPredicateEvaluator {
static final String PE_NAME = "replic";
static final String PN_LAST_REPLICATED_BY = "cq:lastReplicatedBy";
static final String PN_LAST_REPLICATED = "cq:lastReplicated";
static final String PN_LAST_REPLICATED_ACTION = "cq:lastReplicationAction";
static final String PREDICATE_BY = "by";
static final String PREDICATE_SINCE = "since";
static final String PREDICATE_SINCE_OP = " >= ";
static final String PREDICATE_ACTION = "action";
Logger log = LoggerFactory.getLogger(getClass());
/**
* Returns a XPath expression filtering by replication metadata.
*
* @see com.day.cq.search.eval.AbstractPredicateEvaluator#getXPathExpression(com.day.cq.search.Predicate,
* com.day.cq.search.eval.EvaluationContext)
*/
@Override
public String getXPathExpression(Predicate predicate,
EvaluationContext context) {
log.debug("predicate {}", predicate);
String date = predicate.get(PREDICATE_SINCE);
String user = predicate.get(PREDICATE_BY);
String action = predicate.get(PREDICATE_ACTION);
StringBuilder sb = new StringBuilder();
if (date != null) {
sb.append(PN_LAST_REPLICATED).append(PREDICATE_SINCE_OP);
sb.append("xs:dateTime('").append(date).append("')");
}
if (user != null) {
addAndOperator(sb);
sb.append(PN_LAST_REPLICATED_BY);
sb.append("='").append(user).append("'");
}
if (action != null) {
addAndOperator(sb);
sb.append(PN_LAST_REPLICATED_ACTION);
sb.append("='").append(action).append("'");
}
String xpath = sb.toString();
log.debug("xpath **{}**", xpath);
return xpath;
}
/**
* Add an and operator if the builder is not empty.
*
* @param sb a {@link StringBuilder} containing the query under construction
*/
private void addAndOperator(StringBuilder sb) {
if (sb.length() != 0) {
sb.append(" and ");
}
}
}
aem-search-custom-predicate-evaluator - src/main/java/com/adobe/aem/docs/search/ReplicationPredicateEvaluator.java