March 28, 2020
Estimated Post Reading Time ~

AEM Sling Queries

SlingQuery is a sling resource tree traversal tool inspired by the jQuery JavaScript API.

Sling Query is a bundle that is not a part of AEM Felix console till now, but after knowing the bright side of this particular bundle, I am surprised that why it is not the part of AEM.
But not to worry, we can do it by ourselves and take advantage of it.

We can directly upload this bundle in the Felix console and add the dependency in pom.xml

<dependency>
    <groupId>org.apache.sling</groupId>
    <artifactId>org.apache.sling.query</artifactId>
    <version>3.0.0</version>
    <scope> provided</scope>
</dependency>


Why Sling Query?
Sling Query saves us from a lot of code writing. It is good to use this functionality.
It provides our results as an iterator.
useful operations to traverse the resource tree,
flexible filtering syntax,
lazy evaluation of the query result,
SlingQuery object is immutable (thread-safe),
fluent, friendly, jQuery-like API.

When to use Sling Query?
Before using this concept, we need to understand the use cases where the sling query will be best suited.
It is not an alternative to JCR Queries at all. It doesn’t indexes and for a large set of tree traversal, it’s performance is so slow.
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:Pages, using SlingQuery is a bad choice.


Fig- Diff between JCR and Sling Query

Methods list of Sling Query

There are a lot of methods that can be used efficiently to make the programming task much easier.
1).$(resource): Creates a sling query object using passed resource as an initial collection.

Resource resource = resourceResolver.getResource(“/content/we-retail”);
$(resource)

2).children(): fetch all the child resources of a resource.

$(resource).listChildren();

3) .children(String filter): To iterate the children of a resource of particular resource type(here cq:Page) or any other filter condition.

$(resource).children(“cq:Page”);
$(resource).children(“cq:Page[jcr:title=test]”);

4) .closest(String selector): The closest applies on the ancestors of the resource with all the conditions.First it will check the parent of the resource ,than its parents and it will keep on going.

$(resource).closest(“cq:Page”)

5).eq(int index): Sling Query returns a iterator. If there is a need of getting the resource on the basis of index, this function is important.

$(resource).listChildren(“cq:Page”).eq(0);

6).filter(String selector): Filter given collection based on some selector.

final Calendar someTimeAgo = Calendar.getInstance();
someTimeAgo.add(Calendar.HOUR, -5);
// get children pages modified in the last 5 hours
SlingQuery query = $(resource).children("cq:Page").filter(new Predicate<Resource>() { @Override
public boolean accepts(Resource resource) {
return resource.adaptTo(Page.class).getLastModified().after(someTimeAgo);
}});

7) .has(String selector): If the collection has a particular resource based on the selector.

// If the resource fetch all its children having a property value as foundation/components/redirect”
$(resource).children().has("foundation/components/redirect")

8).add(Resource...resource): To add resources in a slingQuery Iterator object.

// Now the SlingQuery iterator will have all the children of a resource and resource itself.
$(resource).children().add($(resource)

9).asList: The iterator of SlingQuery can be converted to a list and get a particular resource on the basis of the index.

// Get the path of the first child resource of a particular resource
$(resource).children("cq:Page").asList().get(0).getPath();

10).find(): search through the matched elements’ child, grandchild, great-grandchild…any levels down.For each resource in collection return all its descendants using selected strategy. Please notice that invoking this method on a resource being a root of a large subtree may and will cause performance problems.

//the resource finds all the richtext resources which can be find in the children,sub-children of the resource
$(resource).find(foundation/components/richtext);

11).next():
// If a resource is having child1, child2 and child3,child2 will be returned.
$(resource).children().first().next();

12).nextAll():
// If a resource is having child1, child2, child3 and child4, child2, child3 and child4 will be returned.
$(resource).children().first().nextAll();

13).nextUntil():Return all following siblings for each resource in the collection up to, but not including, resource matched by a selector.

// let's assume that resource have 4 children: child1, child2, child3 and child4 additionaly, child4 has property jcr:title=Page
// return child2 and child3
$(resource).children().first().nextUntil("[jcr:title=Page]");

14).parent: Return the parent of the resource.
$(resource).parent();

15).parents:For each element in the collection find all of its ancestors, optionally filtering them by a selector.


//create page breadcrumbs for the given resource
$(resource).parents(“cq:Page”);

16) .parentsUntil():For each element in the collection find all of its ancestors until a resource matching the selector is found.

// It will not include that resource which satisfies the condition of cq:page.
$(resource).parentsUntil(“cq:Page”);

17).prev(): Return the previous sibling for each resource in the collection and optionally filter it by a selector. If the selector is given, but the sibling doesn't match it, the empty collection will be returned.

// let's assume that resource have 3 children: child1, child2 and child3
// return child2
$(resource).children().last().prev();

18).prevAll(): Return all preceding siblings for each resource in the collection, optionally filtering them by a selector.

// let's assume that resource have 3 children: child1, child2 and child3
$(resource).children().last().prevAll(); // return child1 and child2

19).prevUntil(): Return all preceding siblings for each resource in the collection up to, but not including, resource matched by a selector.

// let's assume that resource have 4 children: child1, child2, child3 and child4
// additionally, child1 has property jcr:title=Page
$(resource).children().last().prevUntil("[jcr:title=Page]"); // return child2 and child3

20).siblings(): Return siblings for the given resources, optionally filtered by a selector.

$(resource).closest("cq:Page").siblings("cq:Page"); // return all sibling pages
$(resource).children("cq:Page").first().siblings();// return all sibling of first child page

21).slice(): Reduce the collection to a sub-collection specified by a given range. Both from and to are inclusive and 0-based indices. If the to a parameter is not specified, the whole sub-collection starting with will be returned.

// let's assume that resource have 4 children: child1, child2, child3 and child4
$(resource).children().slice(1, 2); // return child1 and child2

22).searchStrategy():Select new search strategy, which will be used in following find() and has() function invocations. There 3 options:
SearchStrategy.DFS - depth-first search
SearchStrategy.BFS - breadth-first search
SearchStrategy.QUERY - use JCR SQL2 query (default since 1.4.0)
DFS and BFS iterate through descendants using an appropriate algorithm. QUERY strategy tries to transform the SlingQuery selector into an SQL2 query and invokes it. Because there are SlingQuery operations that can't be translated (eg. :has() modifier), the SQL2 query result is treated as an initial collection that needs further processing.

$(resource).searchStrategy(SearchStrategy.BFS).find("cq:Page");
$(resource).searchStrategy(SearchStrategy.DFS).find("cq:Page");
$(resource).searchStrategy(SearchStrategy.QUERY).find("cq:Page");

23).first():Filter resource collection to the first element. Equivalent to .eq(0) or .slice(0, 0).
$(resource).siblings().first(); // get the first sibling of the current resource

24).last():Filter resource collection to the last element.
$(resource).siblings().last(); // get the last sibling of the current resource

25).not():Reduce the set of matched elements to those which doesn't match the selector. The selector may contain other modifiers as well, however in this case the function will be evaluated eagerly:

$(resource).children().not("cq:Page"); // remove all cq:Pages from the collection
$(resource).children().not(":first").not(":last"); // remove the first and the last element of the collection

Sling Query Modifiers

The sling Query object is immutable.
$(resource).listChildren().first();

Here three objects gets created ,because slingQuery object is immutable.
$(resource)
$(resource).listChildren()
$(resource).listChildren().first();
Instead of creating many objects, modifiers helps to create less number of objects.
:eq(index) : resource at a particular index can be fetch using eq(index) modifier.

$(resource).find("weretail/components/content/title:eq(0)")

:gt(index) : fetch all the resources greater than the index using gt(index)modifier.

$(resource).children("cq:Page:gt(2)");

:lt(index): fetch all the resources less than the index value using lt(index) modifier.

$(resource).find("weretail/components/content/title:lt(8):first");

:even: fetch all the resources at the even place of the iterator.
$(resource).find("foundation/components/richtext:even");

: odd: fetch all the resources at the odd place of the iterator.
$(resource).find("foundation/components/richtext:odd");

: first: fetch the first resources of the iterator.
$(resource).children("cq:Page:first");

: last: fetch lastresources of the iterator.
$(resource).children("cq:Page:last");

: parent: fetch the parent of the resource.
$(resource).find("foundation/components/richtext:parent");

: has(selector): Reduce the set of the matched elements to those which have descendant matching the selector.
// get children pages containing richtext component
$(resource).children("cq:Page:has(foundation/components/richtext)]");

:not(selector) :Reduce the set of matched elements to those which doesn't match the selector. The selector may contain other modifiers as well, however in this case the function will be evaluated eagerly:
$(resource).children("cq:Page:not(:last)");
References: https://github.com/Cognifide/Sling-Query/wiki/Modifier-list

Operator list
Contains [name*=value]
Contains a word [name~=value]
Has attribute [name]
Starts with [name^=value]
Ends with [name$=value]
Equals [name=value]
Not equal [name!=value]
References: https://github.com/Cognifide/Sling-Query/wiki/Operator-list

Hierarchy operator list
Child operator (parent > child)
Descendant operator (ancestor descendant)
Next adjacent operator (prev + next)
Next siblings operator (prev ~ next)References: https://github.com/Cognifide/Sling-Query/wiki/Hierarchy-operator-list

References:
https://sling.apache.org/documentation/bundles/sling-query.html
https://github.com/Cognifide/Sling-Query/wiki


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.