Header Content Footer
Discover best selling sofas, Lounge chairs and get an extra 10% off using the code SEDONA10

Many times, JCR queries facilitate the work of the AEM developers, also to review a massive change of sling: resourceType or to perform fulltext searches on the web site.

Adobe recommends building as less queries as possible, paying special attention for the production environment, concretely in aem publishes where queries could be used by final client using a functionality of the site. Thinking in performance, using a query always has a price, so when is best to use JCR Queries and when it is better to avoid following Adobe best practices?

JCR Queries are a powerful tool when managed correctly.

  • real end-user queries, such as fulltext searches on content.
  • occasionally where the structured content needs to be found across the entire repository. 

    In such cases, make sure that queries only run when it is absolutely necessary, e.g. on component activation or cache invalidation (as opposed to e.g. Workflows Steps, Event Handlers that trigger on content modifications, Filters, etc).

JCR Queries should never be used for pure rendering requests. For example, JCR Queries are not appropriate for:

  • rendering navigation
  • creating a “top 10 latest news items” overview
  • showing counts of content items 

For rendering content, it is better to traverse the content tree instead of performing a JCR Query.

In this post, the most common used queries will be explored, using the most important predicates provided by the Query Builder API; following an incremental approach does not neglect the possibility of understanding how to build more complex queries. 

1) Find nodes by type under a specific path

type=cq:Page
path=/content/we-retail/language-masters/en

Each query must contains the type of node to search and the scope path. This must be an important topic for avoiding slow queries or does not use OOTB indexes correctly.

As advice for searching pages, elude to use cq:PageContent because is outside default aem indexes.

2) Find nodes using specific property (multivalued or not)

type=cq:Page
path=/content/we-retail/language-masters/en
1_property=jcr:content/@sling:resourceType
1_property.1_value=weretail/components/structure/page
1_property.2_value=weretail/components/structure/product

Predicate property, often is present when describing how to be property or properties for filtering nodes to show as final result.
Basically, it is a predicate built with pairs of property/value.

Using following notation n_value, n+1_value, n+2_value, it is possible to accept several values over property to set up, in other words add a logical OR between values described.

3) Finding nodes with non-empty properties, relative values or not present values.

type=cq:Page
path=/content/we-retail/language-masters/en
1_property=jcr:content/@sling:resourceType
1_property.1_value=weretail/components/structure/page
1_property.2_value=weretail/components/structure/product

2_property=jcr:content/@jcr:title

2_property.operation=exists //opt1

2_property.operation=not //opt2
2_property.value=true //opt2

2_property.operation=like //opt3
2_property.value=E% //opt3


Property/value pairs sometimes is not enough for fulfilling all node result requirements. Operation operator over property predicate is very useful in special cases, when it is necessary find node results with a property always defined (op1), or conversely property must not appears with a concrete value (op2) or in a worst scenario it is not clear value to describe over property (op3).

Opt3 Ex: ‘E%’ ( wild card 0 or more characters ) means a value starts with E.

Operator “like”, must be used with care because it can causes a slow query. Maybe it is necessary to modify the approach iwhen used in a production environment.

4) Fulltext queries 

type=cq:Page
path=/content/we-retail/language-masters/en
1_property=jcr:content/@sling:resourceType
1_property.1_value=weretail/components/structure/page
1_property.2_value=weretail/components/structure/product
fulltext = men
fulltext.relPath = jcr:content/root/responsivegrid/button/@label

Before this section, property scope was clear, but occasionally finding a string over all nodes is necessary, in this case, the developer can define a string value over fulltext predicate will be the best ally. It will find all nodes that contain a specific string over all nodes, or a specific one if it is defined relPath for limiting node scope.

5) Queries using groups

0_group.p.or=true
0_group.1_group.type=dam:AssetContent
0_group.1_group.path=/content/dam/we-retail
0_group.1_group.property=metadata/@dc:format
0_group.1_group.property.value=image/jpeg
0_group.1_group.daterange.property=jcr:lastModified
0_group.1_group.daterange.lowerBound=2015-04-18
0_group.1_group.daterange.lowerOperation=>=
0_group.1_group.1_group.p.or=true
0_group.1_group.1_group.1_fulltext=men
0_group.1_group.1_group.2_fulltext=woman
0_group.2_group.type=cq:PageContent
0_group.2_group.1_group.p.or=true
0_group.2_group.1_group.1_group.1_path=/content/we-retail/language-masters/en
0_group.2_group.1_group.2_group.2_path=/content/we-retail/language-masters/it
0_group.2_group.1_group.1_group.2_property=@hideSubItemsInNav
0_group.2_group.1_group.1_group.2_property.value=true
0_group.2_group.1_group.1_group.2_property.operation=not
0_group.2_group.1_group.2_group.1_property=@jcr:title
0_group.2_group.1_group.2_group.1_property.operation=exists
0_group.2_group.2_group.1_property=@sling:resourceType
0_group.2_group.2_group.1_property.1_value=weretail/components/structure/page
0_group.2_group.2_group.1_property.2_value=weretail/components/structure/product
0_group.2_group.3_group.2_group.p.or=true
0_group.2_group.3_group.2_group.1_fulltext=men
0_group.2_group.3_group.2_group.2_fulltext=woman
orderby=jcr:lastModified
orderby.sort=desc

In the most difficult scenarios, it is necessary to join logically several predicates, like adding an invisible parenthesis creating dependencies between subqueries creating powerful filters retrieving node results.

In order to create a query as complex as example #1, it is mandatory to follow an incremental approach joining groups to reach desire result.

More deep

0_group.1_group = Return nodes dam:AssetContent under /content/dam/we-retail path, modified (jcr:lastModified) after 2015-04-18 that represent an image with mimeType image/jpeg and contains men or woman text inside any of his properties.

0_group.2_group.1_group.1_group = Return content pages nodes (cq:PageContent) from english we-retail site where property hideSubItemsInNav has not got “true” value, his sling:resourceType could be weretail/components/structure/page or weretail/components/structure/product and contains in any of his properties text men or woman.

0_group.2_group.1_group.2_group = Return content pages nodes (cq:PageContent) from italian we-retail site where property jcr:title exists, his sling:resourceType could be weretail/components/structure/page or weretail/components/structure/product and contains in any of his properties men or woman.

Common: All results will be ordered by jcr:lastModified in a descending way.

Note: Predicates under 0_group.2_group are shared in cascade to 0_group.2_group.1_group.1_group & 0_group.2_group.1_group.2_group
more deep groups, for avoiding repeat information.

Final Tips

  • Create an oak index, if it is necessary, see: (https://aemcorner.com/search-in-aem/
  • Empty spaces between predicates throw errors. Be careful with copy and paste!
  • Query tool supports a determinated number of predicates, after that it is possible to look at false errors so you will need to check directly  over java code due query tool will not able to handle it. 
  • For building complex queries, an incremental approach must be used by checking XPATH result which is usually quite intuitive to read logically if what has been written corresponds to what is needed.