What order are the results returned in from TagManager.find()?
I am trying to implement a search for multiple tags, where pages which match more tags appear higher in the list, and pages with fewer matching tags appear lower in the list.
For example, if I am searching for the tags "Apple", "Banana", and "Cherry": If any page has all three of these tags, it should appear before any page which has any two of the tags, which appears before any page with only 1 of the tags. Is this possible using any of the AEM APIs?
Solved! Go to Solution.
Views
Replies
Total Likes
I have discovered that I can use fulltext queries against the tags and then sort by jcr:score. I'm leaving this here in the hopes that someone else finds this useful in the future. The idea is that this query powers a "Related Pages" component that exists at the bottom of my blog article pages in my site.
Here's the query that I am using:
path=/content/mysite/home
type=cq:Page
p.hits=full
fulltext=true # Not sure if this is required or if this is searching for the word 'true'
# I have a field on all pages for "Hide from Search"
1_group.1_boolproperty=@jcr:content/hide
1_group.1_boolproperty.value=false
# exclude the current page from the search, as we're looking for pages related to this page
2_group.p.not=true
2_group.path=/content/mysite/home/current/page
2_group.path.self=true
# search for tags or specific template... each match increases the score
3_group.p.or=true
3_group.1_fulltext=mysite:tag-namespace/tag-1
3_group.1_fulltext.relPath=jcr:content/@cq:tags
3_group.2_fulltext=mysite:tag-namespace/tag-2
3_group.2_fulltext.relPath=jcr:content/@cq:tags
3_group.3_fulltext=mysite:tag-namespace/tag-3
3_group.3_fulltext.relPath=jcr:content/@cq:tags
3_group.4_fulltext=mysite:tag-namespace/tag-4
3_group.4_fulltext.relPath=jcr:content/@cq:tags
3_group.5_property=@jcr:content/cq:template # back-fill by template type
3_group.5_property.1_value=/conf/mysite/path/to/template/type
# each hit's score is calculated based on the fulltext results. Higher score = higher relevancy
4_orderby=@jcr:score
4_orderby.sort=desc
There are a few nuances here:
With some testing on my local machine, this appears to achieve the correct result.
Hi @dylanmccurry ,
I think the best way to approach this is the QueryBuilder API.
SearchResult result = query.getResult();
Collections.sort(result.getHits(), new Comparator<Hit>() {
@Override
public int compare(Hit hit1, Hit hit2) {
int score1 = hit1.getScore();
int score2 = hit2.getScore();
return Integer.compare(score2, score1);
}
});
Query Builder Reference : https://github.com/paulrohrbeck/aem-links/blob/master/querybuilder_cheatsheet.md
I noticed the getScore() method, but when I tried using this it returned the same value for all results. Is there a trick to getting it working?
Yes, my bad.
The getScore won't help in this, we would need to write a custom function instead of a getScore, which will check how many tags it contains.
Something like :
for (String tag : tags) {
if (hit.getProperties().get("cq:tags", String[].class) != null && Arrays.asList(hit.getProperties().get("cq:tags", String[].class)).contains(tag)) {
count++;
}
}
You can use this instead in the comparator.
Edit: The tags is the array of the tags which you need to check , on which you have build the query initially
I see where you can sort by @jcr:score, but it doesn't seem to do anything.
What about fulltext searching multiple keywords (e.g., each tag), on the tag field directly? Or would that not work?
I will also give this a shot but I was hoping to not have to manipulate the results in memory and instead let the index handle it, as there's an unknown number of pages which could be returned and I want only the top 8 or so most relevant to the current page.
jcr:score is open to implementation ( https://developer.adobe.com/experience-manager/reference-materials/spec/jcr/1.0/6.6.5.3_jcr_score_fu... ), so you would need to write more code to support your case.
Fulltext won't give you what you need, it cannot look for a certain property, it looks for keywords.
You can limit the query if that is the case, if you have any specific conditions which make it relevant to the current page, you can add them in the query.
If you don't have something like that, the best way I can think of is actually sending out multiple queries one by one.
I think you can get a loop going which will keep replacing "OR" for "AND", but it also needs lots of code.
My recommendation:
Try to get all the pages, it should be fine, if they are slow, you should create a custom index.
I have tried with your scenario and working as expected with below code.
You can pass third parameter as true in .find() method.
resourceResolver = request.getResourceResolver();
//Tags absolute path as Tag ID
// You can pass tag ID as well
String[] allTags = {"we-retail:activity/biking", "we-retail:activity/hiking", "we-retail:activity/running"};
//agManager instance
TagManager tagManager = resourceResolver.adaptTo(TagManager.class);
//Range Iterator
RangeIterator<Resource> resourceRangeIterator = tagManager.find("/content/learning", allTags, true);
while (resourceRangeIterator.hasNext()) {
Resource result = resourceRangeIterator.next();
String path = result.getPath();
//Custom Code Implementation
}
Regards,
Shiv
I have discovered that I can use fulltext queries against the tags and then sort by jcr:score. I'm leaving this here in the hopes that someone else finds this useful in the future. The idea is that this query powers a "Related Pages" component that exists at the bottom of my blog article pages in my site.
Here's the query that I am using:
path=/content/mysite/home
type=cq:Page
p.hits=full
fulltext=true # Not sure if this is required or if this is searching for the word 'true'
# I have a field on all pages for "Hide from Search"
1_group.1_boolproperty=@jcr:content/hide
1_group.1_boolproperty.value=false
# exclude the current page from the search, as we're looking for pages related to this page
2_group.p.not=true
2_group.path=/content/mysite/home/current/page
2_group.path.self=true
# search for tags or specific template... each match increases the score
3_group.p.or=true
3_group.1_fulltext=mysite:tag-namespace/tag-1
3_group.1_fulltext.relPath=jcr:content/@cq:tags
3_group.2_fulltext=mysite:tag-namespace/tag-2
3_group.2_fulltext.relPath=jcr:content/@cq:tags
3_group.3_fulltext=mysite:tag-namespace/tag-3
3_group.3_fulltext.relPath=jcr:content/@cq:tags
3_group.4_fulltext=mysite:tag-namespace/tag-4
3_group.4_fulltext.relPath=jcr:content/@cq:tags
3_group.5_property=@jcr:content/cq:template # back-fill by template type
3_group.5_property.1_value=/conf/mysite/path/to/template/type
# each hit's score is calculated based on the fulltext results. Higher score = higher relevancy
4_orderby=@jcr:score
4_orderby.sort=desc
There are a few nuances here:
With some testing on my local machine, this appears to achieve the correct result.
Views
Likes
Replies
Views
Likes
Replies
Views
Likes
Replies