AEM Effective Pagination with Query Builder API

There are many ways to implement pagination for AEM content. Effectively choosing the right solution will indeed Bu decrease development time, and promote a performant experience to your end users. AEM has multiple search API’s like xPath, JCR SQL2, Node JCR, Query Builder, etc… But in my opinion, the Query Builder API would be the most effective decision when building a solution with pagination. In AEM, the Query Builder API has emerged as the preferred solution for creating pagination experiences. In this article, we will explore why the Query Builder API stands out as the go-to choice for pagination in AEM.


1. Understanding Pagination in AEM

What is pagination? In general, pagination is a process that divides large sets of content into smaller, more manageable sections, commonly referred to as “pages”; but however, with AEM, anything can be paginated. By doing so, users can navigate through content easily, without being presented with an exhaustive list all at once. In AEM, pagination is particularly crucial for content-heavy websites or applications, ensuring that visitors can access desired information quickly. You can probably imagine, when the page first loads, you can see the first 10 results, and clicking on “next”, you are able to see the next 10 results, and so on…


2. The Query Builder API: An Overview

The Query Builder API is a powerful tool within AEM that allows developers to construct complex queries to retrieve content from the AEM repository. It is designed to provide a more flexible and efficient way of searching and fetching content than traditional JCR-SQL2 queries. The Query Builder API abstracts the underlying technology and provides an easy-to-use interface to interact with the AEM repository.


3. Benefits of the Query Builder API for Pagination

3a. Performance Optimization

One of the primary reasons the Query Builder API is preferred for pagination in AEM is its ability to optimize performance. When dealing with large repositories, traditional queries can be resource-intensive and slow, impacting the page loading time and user experience. The Query Builder API, on the other hand, leverages optimized query execution, resulting in faster response times and enhanced page performance.

3b. Flexibility and Customization

The Query Builder API offers developers a wide range of parameters and options to fine-tune their queries. This level of flexibility enables developers to customize the pagination logic based on specific use cases. Whether it’s setting the number of items per page, defining sorting criteria, or filtering results, the Query Builder API empowers developers to tailor pagination experiences according to project requirements.

3c. Support for Dynamic Content

In dynamic environments where content changes frequently, pagination needs to adapt to reflect these updates accurately. The Query Builder API can seamlessly handle dynamic content by providing real-time results based on the most recent data in the AEM repository. This ensures that the user always receives the latest content, maintaining an up-to-date and engaging user experience.

3d. Facilitating Search Functionality

Pagination is often used in conjunction with search functionality to improve content discovery. The Query Builder API’s capability to perform complex searches across various content attributes makes it a natural fit for integrating search with pagination. By combining these features, users can easily find and navigate to the exact content they need.


4. Pagination and More Features with Query Builder API

4a. Offset and Limit

The Query Builder API allows developers to define the starting point (offset) and the maximum number of items (limit) to fetch for each page. This enables seamless navigation between pages, allowing users to access content beyond the first page of results.

4b. Sorting

To enhance user experience, pagination often goes hand in hand with sorting. The Query Builder API allows developers to specify sorting criteria based on content attributes. For example, you can sort content by date, popularity, or any other relevant property to ensure a logical order for the paginated results.

4c. Search Filters

Incorporating search filters along with pagination empowers users to refine their search queries and find specific content efficiently. The Query Builder API enables developers to apply various filters and constraints to narrow down the results, creating a more personalized experience for users.

4d. Query Aggregation

The Query Builder API also supports query aggregation, which enables developers to group results based on specific attributes. This feature is particularly useful when presenting aggregated data, such as categories or tags, in a paginated manner.

4e. Content Pre-fetching

To further optimize user experience, the Query Builder API allows developers to implement content pre-fetching. By fetching additional content in the background, users experience faster loading times when navigating between pages, reducing any perceived delays.


5. A Simple Servlet for Querying Pages in Java

Below is an example of a simple Java servlet that uses the Query Builder API to query pages from the AEM repository for pagination purposes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceRanking;
import org.osgi.service.component.annotations.Reference;

import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

@Component(service = Servlet.class,
    property = {
        "sling.servlet.methods=GET",
        "sling.servlet.paths=/bin/findPagePaths",
        "sling.servlet.extensions=json"
    })
@ServiceRanking(-1001) // Adjust the service ranking as per your requirement
public class PaginatedPagesServlet extends SlingAllMethodsServlet {
    private static final long serialVersionUID = 1L;

    @Reference
    private QueryBuilder queryBuilder;

    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        // Get request parameters for search and pagination
        String searchTerm = request.getParameter("q");
        int currentPage = Integer.parseInt(request.getParameter("page"));
        int pageSize = 10; // Number of items per page

        // Calculate the offset to start retrieving items
        int offset = (currentPage - 1) * pageSize;

        // Build the query map for the QueryBuilder API
        Map<String, String> queryMap = new HashMap<>();
        queryMap.put("path", "/content"); // Replace with your desired root path
        queryMap.put("type", "cq:Page");
        queryMap.put("fulltext.relPath", "jcr:content");
        queryMap.put("fulltext", searchTerm); // Use the "q" parameter for search terms
        queryMap.put("p.offset", String.valueOf(offset));
        queryMap.put("p.limit", String.valueOf(pageSize));

        // Execute the query
        Query query = queryBuilder.createQuery(PredicateGroup.create(queryMap), request.getResourceResolver().adaptTo(Session.class));
        SearchResult result = query.getResult();

        // Get the paths of the pages from the search result using GJSON
        JSONArray pathsArray = new JSONArray();
        result.getHits().forEach(hit -> {
            String path = hit.getResource().getPath();
            pathsArray.add(path);
        });

        // Send the paginated content (paths of the pages) to the response
        response.setContentType("application/json");
        response.getWriter().write(pathsArray.toJSONString());
    }
}

Curl

1
2
3
4
5
6
7
8
9
# Replace "http://your_server" with the base URL of your AEM instance.
# Replace "/bin/findPagePaths?q=search_term&page=1" with the path to your servlet,
# the desired search term (search_term), and the desired page number.

# Search with "your_search_term" and get results for Page 1
curl -X GET "http://your_server/bin/findPagePaths?q=your_search_term&page=1"

# Search with "another_search_term" and get results for Page 2
curl -X GET "http://your_server/bin/findPagePaths?q=another_search_term&page=2"

5. Conclusion

In conclusion, the Query Builder API in Adobe Experience Manager is a versatile and powerful tool that provides an ideal solution for implementing pagination experiences. With its performance optimization, flexibility, and support for dynamic content, the Query Builder API ensures that users can navigate through content-rich websites or applications seamlessly.

By incorporating features like offset and limit, sorting, search filters, query aggregation, and content pre-fetching, developers can create highly customizable and efficient pagination experiences for their AEM projects. Additionally, the provided Java servlet example demonstrates how the Query Builder API can be used to retrieve paginated content from the AEM repository. As AEM continues to evolve, leveraging the Query Builder API will remain essential for maintaining an excellent user experience and keeping users engaged with your content.


Hello, I am an enthusiastic Adobe Community Advisor and a seasoned Lead AEM Developer. I am currently serving as an AEM Technical Lead at MNPDigital.ca, bringing over a decade of extensive web engineering experience and more than eight years of practical AEM experience to the table. My goal is to give back to the AEM Full Stack Development community by sharing my wealth of knowledge with others. You can connect with me on LinkedIn.

Leave a Reply

Your email address will not be published. Required fields are marked *


Back To Top