Problem Statement:
What is the best way to list all the children in AEM?
Stream-based VS page.listChildren VS Query Builder
Introduction:
AEM Sling Query is a resource traversal tool recommended for content traversal in AEM. Traversal using listChildren(), getChildren(), or the Resource API is preferable to writing JCR Queries as querying can be more costly than traversal. Sling Query is not a replacement for JCR Queries. When traversal involves checking multiple levels down, Sling Query is recommended because it involves lazy evaluation of query results.
JCR queries in AEM development and recommends using them sparingly in production environments due to performance concerns. JCR queries are suitable for end-user searches and structured content retrieval but should not be used for rendering requests such as navigation or content counts.
How can I get all the child pages in AEM using JCR Query?
List<String> queryList = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("path", resource.getPath());
map.put("type", "cq:PageContent");
map.put("p.limit", "-1");
Session session = resolver.adaptTo(Session.class);
Query query = queryBuilder.createQuery(PredicateGroup.create(map), session);
SearchResult result = query.getResult();
ResourceResolver leakingResourceResolverReference = null;
try {
for (final Hit hit : result.getHits()) {
if (leakingResourceResolverReference == null) {
leakingResourceResolverReference = hit.getResource().getResourceResolver();
}
queryList.add(hit.getPath());
}
} catch (RepositoryException e) {
log.error("Error collecting inherited section search results", e);
} finally {
if (leakingResourceResolverReference != null) {
leakingResourceResolverReference.close();
}
}
But JCR Query consumes more resources
AEM recommends using Page.listchildren because of less complexity
List<String> pageList = new ArrayList<>();
Page page = resource.adaptTo(Page.class);
Iterator<Page> childIterator = page.listChildren(new PageFilter(), true);
StreamSupport.stream(((Iterable<Page>) () -> childIterator).spliterator(), false).forEach( r -> {
pageList.add(r.getPath());
}
);
But it sometimes misses some results in the result set and it’s slower compared to Java streams based
How about Java streams?
Java streams can iterate faster and execute faster and consumes very few resources
List<String> streamList = new ArrayList<>();
for (Resource descendant : (Iterable<? extends Resource>) traverse(resource)::iterator) {
streamList.add(descendant.getPath());
}
private Stream<Resource> traverse(@NotNull Resource resourceRoot) {
Stream<Resource> children = StreamSupport.stream(resourceRoot.getChildren().spliterator(), false)
.filter(this::shouldFollow);
return Stream.concat(
shouldInclude(resourceRoot) ? Stream.of(resourceRoot) : Stream.empty(),
children.flatMap(this::traverse)
);
}
protected boolean shouldFollow(@NotNull Resource resource) {
return !JcrConstants.JCR_CONTENT.equals(resource.getName());
}
protected boolean shouldInclude(@NotNull Resource resource) {
return resource.getChild(JcrConstants.JCR_CONTENT) != null;
}
I recently came across this logic while debugging the OOTB sling sitemap generator: https://github.com/apache/sling-org-apache-sling-sitemap

Stream-based results took just 3miliseconds compared to page.listChildren or query