Relevance in TagManager.find()? | Community
Skip to main content
Level 4
May 8, 2023
Solved

Relevance in TagManager.find()?

  • May 8, 2023
  • 3 replies
  • 2469 views

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?

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by dylanmccurry

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:

  1. For whatever reason, fulltext searching on tags requires the relPath of `jcr:content/@cq:tags` ... not sure why the @ is required at the cq:tags and not the jcr:content.  Actually I'm not sure what the @ represents and when and where it's necessary.  Maybe someone can chime in and let me know?
  2. I am opening an OR condition and doing a bunch of full text searches with the last condition be a template filter.  This achieves the back-fill effect where i'm always guaranteed to get results so long as pages exist
  3. You can sort by @6655266:score, but it only really works if you are enabling fulltext searching.  In this case, I am just fulltext searching the name of the tag in the tags field

 

With some testing on my local machine, this appears to achieve the correct result.

3 replies

Anmol_Bhardwaj
Community Advisor
Community Advisor
May 8, 2023

Hi @dylanmccurry ,

 

I think the best way to approach this is the QueryBuilder API.

  1. You can create a query in which you can mention an "OR" for the tags.
  2. And then once you get the result, you can get the hits (each hit).
  3. And then traverse through them
  4. Get the score for each hit, and compare them in a comparator or manually.
  5. And then you can sort them as you like.
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 

 

Level 4
May 8, 2023

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?

Anmol_Bhardwaj
Community Advisor
Community Advisor
May 8, 2023

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

Shiv_Prakash_Patel
Community Advisor
Community Advisor
May 8, 2023

Hi @dylanmccurry 

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

 

Shiv Prakash
dylanmccurryAuthorAccepted solution
Level 4
May 9, 2023

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:

  1. For whatever reason, fulltext searching on tags requires the relPath of `jcr:content/@cq:tags` ... not sure why the @ is required at the cq:tags and not the jcr:content.  Actually I'm not sure what the @ represents and when and where it's necessary.  Maybe someone can chime in and let me know?
  2. I am opening an OR condition and doing a bunch of full text searches with the last condition be a template filter.  This achieves the back-fill effect where i'm always guaranteed to get results so long as pages exist
  3. You can sort by @6655266:score, but it only really works if you are enabling fulltext searching.  In this case, I am just fulltext searching the name of the tag in the tags field

 

With some testing on my local machine, this appears to achieve the correct result.