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.