Expand my Community achievements bar.

July 31st AEM Gems Webinar: Elevate your AEM development to master the integration of private GitHub repositories within AEM Cloud Manager.
SOLVED

Sortby two Values at a time

Avatar

Level 2

Hi Community,

I am trying to sort by two kinds of Pages together. Basically, I am performing a search on pages that have articles component and pages that do not have articles (brought in new)

Earlier the sort was happening based on the articleDate. Now, that I have pages without articles. I have a page property called articleDate.

And I want to sort both of them combined together in Descending order.

 

I tried the below predicates :

predicateMap.put("9_orderby", "@jcr:content/articleDate");
predicateMap.put("9_orderby.sort", "desc");
predicateMap.put("10_orderby", "@articleDate");
predicateMap.put("10_orderby.sort", "desc");

The above code was ordering results based on page property : article Date first , followed by results that were ordered by articleDate of the article.

But I want them to be combined purely based on date.

When I tried 

9_orderby = @jcr:content/articleDate, @articleDate
9_orderby.sort = desc

it is not giving me the order expected. it gives sept 9, 2021 first and then feb 3, 2024

. Can someone help me on this?

1 Accepted Solution

Avatar

Correct answer by
Level 2

We have implemented a custom Predicate that has solved this issue. Providing the sample code here for the above use case. 

import com.day.cq.search.eval.AbstractPredicateEvaluator;
import com.day.cq.search.eval.EvaluationContext;
import org.osgi.service.component.annotations.Component;

import com.day.cq.search.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;
import javax.jcr.query.Row;
import java.util.Calendar;
import java.util.Comparator;

@Component(factory = "com.day.cq.search.eval.PredicateEvaluator/orderbyarticlepredicate")
public class OrderByArticlePredicate extends AbstractPredicateEvaluator {

private static final Logger LOG = LoggerFactory.getLogger(OrderByArticlePredicate.class);

@Override
public Comparator<Row> getOrderByComparator(final Predicate predicate, final EvaluationContext context) {
return new Comparator<Row>() {
@Override
public int compare(Row o1, Row o2) {
Calendar o1ArticleDate = Calendar.getInstance();
Calendar o2ArticleDate = Calendar.getInstance();
o1ArticleDate.setTimeInMillis(0);
o2ArticleDate.setTimeInMillis(0);

try {

if (o1.getNode().hasProperty("articleDate")) {
o1ArticleDate = o1.getNode().getProperty("articleDate").getDate();
} else {
if (o1.getNode().hasProperty("jcr:content/articleDate")) {
o1ArticleDate = o1.getNode().getProperty("jcr:content/articleDate").getDate();
}
}

if (o2.getNode().hasProperty("articleDate")) {
o2ArticleDate = o2.getNode().getProperty("articleDate").getDate();
} else {
if (o2.getNode().hasProperty("jcr:content/articleDate")) {
o2ArticleDate = o2.getNode().getProperty("jcr:content/articleDate").getDate();
}
}

} catch (RepositoryException e) {
LOG.error("Exception comparing article dates in query", e);
}

return o1ArticleDate.compareTo(o2ArticleDate);
}
};
}

}

View solution in original post

8 Replies

Avatar

Community Advisor

Hi @kiran_uhc 
You can't sort results based on 2 values, you have to write your own logic and reorder results using java.



Arun Patidar

Avatar

Level 9

@kiran_uhc As per documentation, it should be working. This is what I am referring  to orderby 

Allows the results to be sorted. If ordering by multiple properties is required, this predicate must be added multiple times using the number prefix, such as 1_orderby=first, 2_oderby=second.

Are you certain that these properties exists at the path you are specifying-

predicateMap.put("9_orderby", "@jcr:content/articleDate");
predicateMap.put("9_orderby.sort", "desc");
predicateMap.put("10_orderby", "@articleDate"); -- This can be just articleDate (remove @ prefix). Also the number i.e 9_,10_ etc, ensure they are unique for each predicate and not just for orderby
Please check your properties once again and make some adjustments if needed, you can refer the documentation as well (which is not very detailed but seems to suggest that multiple property sort works).

thanks.

Avatar

Level 2

These predicates I gave are unique Kamal. It is actually a large set of predicates. I have added just the orderby part alone.

 

Yes, documentation suggests that it works. But in real in doesnt.

Avatar

Level 9

@kiran_uhc : You can raise a Adobe Care ticket to check why this doesn't work when it is mentioned specifically in the documentation.
thanks.

Avatar

Employee

Here’s a possible solution leveraging AEM's query API capabilities and custom application logic:

1. Use a single `orderby` predicate that targets the primary date property available on most pages (for example, `@jcr:content/articleDate`).
2. Execute the query to retrieve an initial result set.
3. Post-process the result set in your application code to adjust the sorting by:
- Checking if the `articleDate` property exists on the retrieved nodes.
- For nodes missing the `articleDate` property, use the `@articleDate` page property.
- Create a unified list of nodes with their respective date values.
- Sort this list based on the date values in descending order.

Here is a pseudo-code example of how you might implement this logic:

```java
Map pageDateMap = new LinkedHashMap<>();
for (Hit hit : result.getHits()) {
Node pageNode = hit.getNode();
Calendar date = null;

// Check if the 'articleDate' property exists within jcr:content
if (pageNode.hasProperty("jcr:content/articleDate")) {
date = pageNode.getProperty("jcr:content/articleDate").getDate();
}
// If not, use the 'articleDate' page property
else if (pageNode.hasProperty("articleDate")) {
date = pageNode.getProperty("articleDate").getDate();
}

// Add to a map to keep track of pages and their dates
if (date != null) {
pageDateMap.put(pageNode.getPath(), date);
}
}

// Sort the map entries by their date values in descending order
List> entries = new ArrayList<>(pageDateMap.entrySet());
Collections.sort(entries, new Comparator>() {
public int compare(Map.Entry a, Map.Entry b) {
return b.getValue().compareTo(a.getValue());
}
});

// Now 'entries' contains sorted pages by date in descending order
```

This code snippet assumes that you have a result set from an AEM query and proceeds to sort the pages by their date properties, whether the date is within the `jcr:content` node or directly on the page node.

Avatar

Level 2

Thanks for the solution & pseudocode Krishna,

Actually I am implementing Lucene Search for my overall Site and it is part of that predicates that I mentioned.

above. If it is a small set of results. Then this custom logic would have helped. But mine is a large set and is also based on pagination. So, I guess this wouldn't help

Avatar

Administrator

@kiran_uhc Did you find the suggestions from users helpful? Please let us know if more information is required. Otherwise, please mark the answer as correct for posterity. If you have found out solution yourself, please share it with the community.



Kautuk Sahni

Avatar

Correct answer by
Level 2

We have implemented a custom Predicate that has solved this issue. Providing the sample code here for the above use case. 

import com.day.cq.search.eval.AbstractPredicateEvaluator;
import com.day.cq.search.eval.EvaluationContext;
import org.osgi.service.component.annotations.Component;

import com.day.cq.search.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;
import javax.jcr.query.Row;
import java.util.Calendar;
import java.util.Comparator;

@Component(factory = "com.day.cq.search.eval.PredicateEvaluator/orderbyarticlepredicate")
public class OrderByArticlePredicate extends AbstractPredicateEvaluator {

private static final Logger LOG = LoggerFactory.getLogger(OrderByArticlePredicate.class);

@Override
public Comparator<Row> getOrderByComparator(final Predicate predicate, final EvaluationContext context) {
return new Comparator<Row>() {
@Override
public int compare(Row o1, Row o2) {
Calendar o1ArticleDate = Calendar.getInstance();
Calendar o2ArticleDate = Calendar.getInstance();
o1ArticleDate.setTimeInMillis(0);
o2ArticleDate.setTimeInMillis(0);

try {

if (o1.getNode().hasProperty("articleDate")) {
o1ArticleDate = o1.getNode().getProperty("articleDate").getDate();
} else {
if (o1.getNode().hasProperty("jcr:content/articleDate")) {
o1ArticleDate = o1.getNode().getProperty("jcr:content/articleDate").getDate();
}
}

if (o2.getNode().hasProperty("articleDate")) {
o2ArticleDate = o2.getNode().getProperty("articleDate").getDate();
} else {
if (o2.getNode().hasProperty("jcr:content/articleDate")) {
o2ArticleDate = o2.getNode().getProperty("jcr:content/articleDate").getDate();
}
}

} catch (RepositoryException e) {
LOG.error("Exception comparing article dates in query", e);
}

return o1ArticleDate.compareTo(o2ArticleDate);
}
};
}

}