AEM Sling Query : Comprehensive Guide
Sling Query is basically a Sling resource tree traversal tool inspired by the jQuery JavaScript API created by Tomek Rękawek and contributed to Apache. Sling Query is very effective when we need to traverse through content tree in AEM.
In this tutorial i will try to cover below topic:-
- Introduction to Sling Query
- When to Use Sling Query
- Sling Query vs JCR Query
- How to include Sling Query in AEM
- Sling Query Examples
Introduction to Sling Query:-
As mentioned above AEM Sling Query is a resource traversal tool. In AEM for searching a content it is always recommended to go for traversal using listChildren() or getChildren() or Resource API instead of writing JCR Queries because performing query is always a costly operation as compared with traversal, depending upon the requirement. Sling Query is not an alternative for JCR Queries that we will see below when to use JCR queries and when we should use Sling Query.
When to Use Sling Query
We are clear now that when we need to traverse the content in AEM , first approach is to handle it with using methods like listChildren() and traverse using loops. But for example we need to traverse 10 level down and check get the first ancestor, writing normal tree traversal using Resource API is very cumbersome because it involves a lot of while()s, iterators and null checks and with time code complexity is also increasing, in such cases it is highly recommended to go for Sling Query, because it involves lazy evaluation of query result means it don’t query resources unless we need them. We will see it as part of one example below.
Traditional way of traversing for resource.
String path = "/content/we-retail/us/en/men/jcr:content/root/responsivegrid";
String homeTemplate = "/conf/we-retail/settings/wcm/templates/hero-page";
Resource resource = resourceResolver.getResource(path);
while ((resource = resource.getParent()) != null) {
if (!resource.isResourceType("cq:Page")) {
continue;
}
ValueMap map = resource.adaptTo(ValueMap.class);
String cqTemplate = map.get("jcr:content/cq:template");
if (homeTemplate.equals(cqTemplate)) {
break;
}
}
resource.getPath();
Traversing content using Sling Query
import static org.apache.sling.query.SlingQuery.$;
Resource resource = getResource("/content/we-retail/us/en/men/jcr:content/root/responsivegrid");
SlingQuery resourceCollection = $(resource)
.closest("cq:Page[jcr:content/cq:template=/conf/we-retail/settings/wcm/templates/hero-page]")
You can clearly see that Sling query takes all code complexity away and comes with lot of predefined Methods, Selectors , Attribute and Modifiers.
For more detail please click here.
Sling Query vs JCR Query
Sling Query is not meant to replace JCR queries (XPath, JCR-SQL, JCR-SQL2). It doesn’t use indexes and generally in queries traversing large subtrees (like / or /content or /content/mysite/en) it’ll be much slower than well written JCR query.
Purpose of the SlingQuery is to provide a convenient way to traverse resource tree. All SlingQuery operations are eventually transformed into a series of listChildren() and getParent()
As a rule of thumb – if you have a complex Java loop reading resource children or parents and processing them somehow, rewriting it to SlingQuery will be a good choice. If you have a recursive method trying to get some resource ancestor, using SlingQuery will be a good choice.
On the other hand, if you have a large resource subtree and want to find all cq:Page
, using SlingQuery is a bad choice.
How to include Sling Query in AEM
Lets consider you have created AEM project using maven archetype. Follow below steps to add Sling Query dependency in AEM.
Open main pom.xml and add below deplendency
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.query</artifactId>
<version>3.0.0</version>
</dependency>
Open core.xml and add below dependency
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.query</artifactId>
</dependency>
As Sling query dependency is not available by default in AEM, so we need t embed it in core pom.xml so that it is available to our bundle after deployment to AEM. You can check weather any dependency is available in AEM or not using depfinder tool available in AEM (http://localhost:4502/system/console/depfinder ).
Embed Sling query dependency in core pom.xml using
-includeresource: org.apache.sling.query-[0-9.]*.jar;lib:=true
<plugin>
<groupId>biz.aQute.bnd</groupId>
<artifactId>bnd-maven-plugin</artifactId>
<executions>
<execution>
<id>bnd-process</id>
<goals>
<goal>bnd-process</goal>
</goals>
<configuration>
<bnd><![CDATA[
Import-Package: javax.annotation;version=0.0.0,*
-includeresource: faspmanager-3.7.2.0.jar;lib:=true
-includeresource: org.apache.sling.query-[0-9.]*.jar;lib:=true
]]></bnd>
</configuration>
</execution>
</executions>
</plugin>
To embed dependency in older version of Maven use below Snnipet
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>aemlearning.*;net.oauth.*;version=20100527</Export-Package>
<Import-Package>*</Import-Package>
<Bundle-SymbolicName>aemlearning</Bundle-SymbolicName>
<Embed-Dependency>oauth-provider,org.apache.sling.query-[0-9.]*.jar;scope=runtime|compile</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
In order to import Sling Query to any Java file using its static import as shown below.
import static org.apache.sling.query.SlingQuery.$;
Note:- Using $() in Java class is a valid method name, because Sling Query is inspired by JQuery that’s why it uses $.
Few best Practices about including dependencies in AEM:-
- All dependencies with version number should go to main pom.xml
- All dependencies with out version number should go to core pom.xml
- Packages like acs commons, custom one goes inside all with version and its dependency should go inside core without version.
- If dependency not available in AEM depfinder, it needs to be embedded in core, so that it is available in export filter.
AEM Sling Query Examples
Get all Title components from the responsive grid
Resource r = getResource("/content/we-retail/us/en/men")
SlingQuery collectionItr = $(r)
.closest("cq:PageContent")
.find("wcm/foundation/components/responsivegrid")
.children("weretail/components/content/title")
Iterator<Resource> childItr = collectionItr.iterator();
while (childItr.hasNext()) {
Resource childRes = childItr.next();
logger.debug(childRes.path);
}
Note:- Each Sling Query method returns new collection and SlingQuery
object implements Iterable
Search strategy example – Find all pages inside a node
Resource r = getResource("/content/we-retail/us/en")
SlingQuery collectionItr = $(r)
.searchStrategy(SearchStrategy.DFS)
.find("cq:Page")
Iterator<Resource> childItr = collectionItr.iterator();
while (childItr.hasNext()) {
Resource childRes = childItr.next();
logger.debug(childRes.path);
}
Note:- There are 3 Search strategy available DFS( depth-first search ), BFS( breadth-first search ), QUERY (use JCR SQL2 query (default since 1.4.0)). DFS and BFS uses traversal where as Query uses JCR SQL2 Query.
Find method vs JCR
-
find()
is powerful but may be dangerous - it should be used only for small subtrees
- if you want to query a large space, use JCR-SQL[2] or XPath
- if your SlingQuery processes more than 100 resources, you’ll get a warning in the logs:
24.02.2020 13:35:49.942 *WARN* [0:0:0:0:0:0:0:1 [1401276949857] POST /bin/groovy
console/post.json HTTP/1.1] SlingQuery Number of processed resources exceeded 10
0. Consider using a JCR query instead of SlingQuery.
Get all pages under a resource
$(resource).closest("cq:Page")
Get first ancestor with a given template
$(resource).closest("cq:Page[jcr:content/cq:template=/conf/we-retail/settings/wcm/templates/hero-page]")
Get the first sibling page of the current page
$(resource).closest("cq:Page").siblings("cq:Page").first()
Get the second child of each resource
$(resource1, resource2, resource3).children(":eq(1)")
Get the first two children of each resource:
$(resource1, resource2, resource3).children(":lt(2)")
Closest ancestor page having non-empty responsivegrid or parsys
$(resource).closest("cq:Page wcm/foundation/components/responsivegrid:parent")
Get all parents of the current resource and adapt them to Page object
Iterable<Page> breadcrumbs = $(resource).parents("cq:Page").map(Page.class);
Use JCR query to find all cq:Page
with a given template
$(resourceResolver)
.searchStrategy(SearchStrategy.QUERY)
.find("cq:PageContent[cq:template=/conf/we-retail/settings/wcm/templates/hero-page]")
.parent()
Find children named en
or de
$(resource).children("#en, #de")
Hope i am able to provide you an overview of what do you mean by aem sling query and how to use them in aem for easy traversing. I will be keep on adding more examples to this post, so that it will be more helpful to community members. Please drop a comment if you have any questions or you have more examples to share with community members. I will add them to this post.
Leave a Reply