Ad Code

Monday, October 9, 2017

Apache Sling :: Sling Query in AEM 6.3


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

Sling Query is a bundle which 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 the advantages 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 a good to use this functionality.
It provides us 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 sling query will be best suited.
It is not an alternative of JCR Queries at all.It doesn’t indexes and for 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.
pros and cons.PNG
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 the 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 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, 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 parameter is not specified, the whole sub-collection starting with from 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:
DFS and BFS iterate through descendants using appropriate algorithm. QUERY strategy tries to transform SlingQuery selector into a 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. 
  1. $(resource).searchStrategy(SearchStrategy.BFS).find("cq:Page");
  2. $(resource).searchStrategy(SearchStrategy.DFS).find("cq:Page");
  3. $(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:
  1. $(resource).children().not("cq:Page"); // remove all cq:Pages from the collection
  2. $(resource).children().not(":first").not(":last"); // remove the first and the last element of the collection

Sling Query Modifiers
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.
  1. :eq(index) : resource at a particular index can be fetch using eq(index) modifier.
$(resource).find("weretail/components/content/title:eq(0)")

  1. :gt(index) : fetch all the resources greater than the index using gt(index)modifier.
$(resource).children("cq:Page:gt(2)");

  1. :lt(index): fetch all the resources less than the index value using lt(index) modifier.
$(resource).find("weretail/components/content/title:lt(8):first");

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

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

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

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

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

  1. :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)]");

  1. :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)");

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]

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:

Demonstration Video On Sling Query:


If you have any query or suggestion then kindly comment or mail us at sgaem.blog02@gmail.com

Hope it will help you guys !!
Thanks and Happy Learning.

4 comments:

  1. Nice Article and video. Thanks

    ReplyDelete
  2. HI Shivani,

    We have created some custom metadata fields by touch UI by creating DAM metadata schema and applying schema to our client specific folder. But we are unable to do the same for classic UI. Unable to create metadata properties for DAM assets in classic UI. Below link is not giving more information
    “https://docs.adobe.com/docs/en/cq/5-6-1/dam/metadata_for_digitalassetmanagement.html#Creating New Metadata Property for Assets”

    Can you give info on how to create DAM metadata properties in Classic UI and applying to client specific folder. (In addition to existing fields, needs to add additional fields. I tried Metadata Template in classic UI , but of no use.)

    ReplyDelete